diff --git a/CODEOWNERS b/CODEOWNERS index 06fc47d3909..9cac4b3e092 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -217,6 +217,7 @@ /bundles/org.openhab.binding.luxtronikheatpump/ @sgiehl /bundles/org.openhab.binding.magentatv/ @markus7017 /bundles/org.openhab.binding.mail/ @J-N-K +/bundles/org.openhab.binding.matter/ @digitaldan /bundles/org.openhab.binding.max/ @marcelrv /bundles/org.openhab.binding.mcd/ @simon-dengler /bundles/org.openhab.binding.mcp23017/ @aogorek diff --git a/bom/openhab-addons/pom.xml b/bom/openhab-addons/pom.xml index 8ee1624743b..045ca269502 100644 --- a/bom/openhab-addons/pom.xml +++ b/bom/openhab-addons/pom.xml @@ -1071,6 +1071,11 @@ org.openhab.binding.mail ${project.version} + + org.openhab.addons.bundles + org.openhab.binding.matter + ${project.version} + org.openhab.addons.bundles org.openhab.binding.max diff --git a/bundles/org.openhab.binding.matter/.gitignore b/bundles/org.openhab.binding.matter/.gitignore new file mode 100644 index 00000000000..8051ab7d085 --- /dev/null +++ b/bundles/org.openhab.binding.matter/.gitignore @@ -0,0 +1,13 @@ +# Node.js dependencies +code-gen/node_modules/ +matter-server/node_modules/ + +# Node.js bin dirs +code-gen/node +matter-server/node + +# Build output directories +code-gen/dist/ +code-gen/out/ +matter-server/dist/ +matter-server/out/ \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/DEVELOPMENT.md b/bundles/org.openhab.binding.matter/DEVELOPMENT.md new file mode 100644 index 00000000000..b09295ea5ba --- /dev/null +++ b/bundles/org.openhab.binding.matter/DEVELOPMENT.md @@ -0,0 +1,214 @@ +# Development Guide + +This document describes how to set up your development environment and contribute to the project. The project consists of three main components: + +1. Code Generation Tool +2. Matter.js WebSocket Service +3. openHAB Java Add-on + +## General Build Requirements + +- Java 17 or higher +- Node.js 18 or higher +- npm 9 or higher + +## Building the Project + +The project uses Maven as the primary build tool. To build all components: + +```bash +mvn clean install +``` + +### Maven Build Process + +The `mvn clean install` command executes several steps to build the websocket server and package everything together. +By default, this will not regenerate the matter cluster classes. To regenerate the cluster classes, use the `code-gen` profile: + +```bash +mvn clean install -P code-gen +``` + +The following maven steps are executed: + +1. **Clean Phase** + - Without `-P code-gen`: Cleans only standard build output directories + - With `-P code-gen`: Additionally cleans: + - The `code-gen/out` directory + - Generated Java classes in `src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen` + +2. **Generate Resources Phase** + - Sets up Node.js and npm environment + - Installs Matter server dependencies + - Builds Matter server using webpack + - Copies built `matter.js` to the appropriate resource directory for inclusion in the final jar + +3. **Generate Sources Phase** (only with `-P code-gen`) + - Runs code generation tool: + 1. Installs code-gen npm dependencies + 2. Runs the main 'app.ts' which uses custom handlebars template for code generation from Matter.js SDK definitions + 3. Moves generated Java classes to `src/main/java/.../internal/client/dto/cluster/gen` + 4. Cleans up temporary output directories + +4. **Process Sources Phase** (only with `-P code-gen`) + - Formats generated code using spotless + +5. **Compile and Package** + - Compiles Java sources + + +## Project Components + +### 1. Code Generation Tool (`code-gen/`) + +#### Purpose +The code generation tool is responsible for creating Java classes from the Matter.js SDK definitions. It processes the Matter protocol specifications and generates type-safe Java code that represents Matter clusters, attributes, and commands. + +#### Architecture +- Located in the `code-gen/` directory +- Uses TypeScript for code generation logic (see `code-gen/app.ts`) +- Utilizes Handlebars templates for Java code generation (see `code-gen/templates`) +- Processes Matter.js SDK definitions directly from the matter.js SDK ( `Matter.children....`) + +#### Building and Running +```bash +cd code-gen +npm install +npm run build +``` + +The generated Java classes will be placed in the openHAB addon's source directory. + +### 2. Matter.js WebSocket Service (`matter-server/`) + +#### Purpose +The Matter.js WebSocket service acts as a bridge between the openHAB binding and the Matter.js SDK. It provides a WebSocket interface that allows the Java binding to communicate with Matter devices through the Matter.js protocol implementation. + +#### Architecture +- WebSocket server implementation in TypeScript +- Two main operation modes: + - Client Controller: Acts as a Matter controller allowing communication with Matter devices + - Bridge Controller: Acts as a Matter bridge node, exposing non matter devices (openHAB items) as endpoints for 3rd party clients to control. This will bind on the default matter port by default. + - Modes are independent of each other and create their own matter instances +- Real-time event system for device state updates + +#### WebSocket Protocol + +##### Connection Establishment +1. Client connects to WebSocket server with query parameters: + - `service`: Either 'bridge' or 'client' + - For client mode: `controllerName` parameter required + - For bridge mode: `uniqueId` parameter required +2. Server validates connection parameters and initializes appropriate controller +3. Server sends 'ready' event when controller is initialized + +##### Message Types + +###### Requests +```typescript +{ + id: string; // Unique request identifier which will be used in the response to track messages + namespace: string; // Command RPC namespace + function: string; // Function to execute in the namespace + args?: any[]; // Optional function arguments +} +``` + +###### Responses +```typescript +{ + type: string; // "response" + message: { + type: string; // "result", "resultError", "resultSuccess" + id: string; // Matching ID from the original request + result?: any; // Operation result (if successful) + error?: string; // Error message (if failed) + } +} +``` + +###### Events +```typescript +{ + type: string; // "event" + message: { + type: string; // Event type (see below) + data?: any; // Event data (string, number, boolean, map, etc....) + } +} +``` + +##### Event Types +- `attributeChanged`: Device attribute value updates +- `eventTriggered`: Device-triggered events +- `nodeStateInformation`: Device connection state changes +- `nodeData`: Device data updates (cluster and attributes) +- `bridgeEvent`: Bridge-related events + +##### Node States +- `Connected`: Node is connected and ready for querying of data +- `Disconnected`: Node is disconnected +- `Reconnecting`: Node is attempting to reconnect (but still offline) +- `WaitingForDeviceDiscovery`: Waiting for MDNS announcement (so still offline) +- `StructureChanged`: Node structure has been modified +- `Decommissioned`: Node has been decommissioned + +#### Components +- `app.ts`: Main server implementation and WebSocket handling + - Manages WebSocket connections + - Handles message routing + - Implements connection lifecycle +- `Controller.ts`: Base abstract controller functionality (implemented by client and bridge) + - Common controller operations + - Message handling framework + - Handles looking up namespaces and functions for remote commands +- `client/`: Matter controller functionality +- `bridge/`: Matter bridge functionality +- `util/`: Shared utilities and helper functions + +#### Building and Running +```bash +cd matter-server +npm install +npm run webpack +``` + +Server configuration options: +- `--port`: WebSocket server port (default: 8888) +- `--host`: Server host address + +#### Error Handling +- Connection errors trigger immediate WebSocket closure +- Operation errors are returned in response messages +- Node state changes are communicated via events +- Automatic reconnection for temporary disconnections +- Parent process monitoring for clean shutdown + +### 3. openHAB Matter Binding (`src/`) + +#### Purpose +The openHAB Matter binding provides integration between openHAB and Matter devices. It implements the openHAB binding framework and communicates with Matter devices through the Matter.js WebSocket service. + +#### Architecture + +##### Shared Client Code +- Location: `src/main/java/.../internal/client/` +- Handles WebSocket communication with Matter server +- Implements message serialization/deserialization +- Manages connection lifecycle + +##### Controller Code +- Location: `src/main/java/.../internal/controller/` +- Implements Matter device control logic +- Manages device state and commands through "converter" classes + +##### Bridge Code +- Location: `src/main/java/.../internal/bridge/` +- Implements openHAB Matter bridge functionality +- Uses Item metadata tags to identity Items to expose (similar to homekit, alexa, ga, etc....) +- Handles device pairing and commissioning of 3rd party controllers (Amazon, Apple, Google, etc.... ) + +#### Building +```bash +mvn clean install +``` \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/NOTICE b/bundles/org.openhab.binding.matter/NOTICE new file mode 100644 index 00000000000..20d57c4ce88 --- /dev/null +++ b/bundles/org.openhab.binding.matter/NOTICE @@ -0,0 +1,20 @@ +This content is produced and maintained by the openHAB project. + +* Project home: https://www.openhab.org + +== Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License 2.0 which is available at +https://www.eclipse.org/legal/epl-2.0/. + +== Source Code + +https://github.com/openhab/openhab-addons + +== Third-party Content + +matter.js +* License: Apache 2.0 +* Project: https://github.com/project-chip/matter.js +* Source: https://github.com/project-chip/matter.js \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/README.md b/bundles/org.openhab.binding.matter/README.md new file mode 100644 index 00000000000..4c1232e3805 --- /dev/null +++ b/bundles/org.openhab.binding.matter/README.md @@ -0,0 +1,535 @@ +# Matter Binding + +The Matter Binding for openHAB allows seamless integration with Matter-compatible devices. + +## Supported functionality + +This binding supports two different types of Matter functionality which operate independently of each other. + +- [Matter Client](#matter-client) + - This allows openHAB to discover and control other Matter devices like lights, thermostats, window coverings, locks, etc... + +- [Matter Bridge](#matter-bridge) + - This allows openHAB to expose items as Matter devices to other Matter clients. + This allows local control of openHAB devices from other ecosystems like Apple Home, Amazon, and Google Home. + +For more information on the Matter specification, see the [Matter Ecosystem Overview](#matter-ecosystem-overview) section at the end of this document. + +## Matter.JS Runtime + +This binding uses the excellent [matter.js](https://github.com/project-chip/matter.js) implementation of the the Matter 1.4 protocol. + +As such, this binding requires NodesJS 18+ and will attempt to download and cache an appropriate version when started if a version is not already installed on the system. +Alpine Linux users (typically docker) and those on older Linux distributions will need to install this manually as the official NodeJS versions are not compatible. + +## Matter and IPv6 + +Matter **requires** IPv6 to be enabled and be routable between openHAB and the Matter device. +This means IPv6 needs to be enabled on the host openHAB is running, and the network must be able route IPv6 unicast and multicast messages. +Docker, VLANs, subnets and other configurations can prohibit Matter from working if not configured correctly. + + +# Matter Client + +This describes the Matter controller functionality for discovering and controlling Matter devices. + +## Supported Things + +The Matter Binding supports the following types of things: + +- `controller`: The main controller that interfaces with Matter devices. +It requires the configuration parameter `nodeId` which sets the local Matter node ID for this controller (must be unique in the fabric). +**This must be added manually.** +- `node`: Represents an individual Node within the Matter network. +The only configuration parameter is `nodeId`. +A standard Node will map Matter endpoints to openHAB channel groups. +**This will be discovered automatically** when a pairing code is used to scan for a device and should not be added manually. +- `endpoint`: Represents an standalone endpoint as a child of a `node` thing. Only Endpoints exposed by Matter bridges will be added as `endpoint` things, otherwise Matter Endpoints are mapped on a `node` thing as channel groups. An `endpoint` thing **will be discovered automatically** when a node is added that has multiple bridged endpoints and should not be added manually. + +## Discovery + +Matter controllers must be added manually. +Nodes (devices) will be discovered when a `pairCode` is used to search for a device to add. +Bridged endpoints will be added to the inbox once the parent Node is added as a thing. + +### Device Pairing: General + +The pairing action can be found in the settings of the "Controller" thing under the "Actions" -> "Pair Matter Device" + +Matter Pairing + +This action will give feedback on the pairing process, if successful a device will be added to the Inbox. + +See [Device Pairing: Code Types](#device-pairing-code-types) for more information on pairing codes and code formats. + +The same codes can also be used in the openHAB Thing discovery UI, although feedback is limited and only a single controller is supported. + +Thing Discovery + +### Device Pairing: Code Types + +In order to pair (commission in matter terminology) a device, you must have an 11 digit manual pairing code (eg 123-4567-8901 or 12345678901) or a QR Code (eg MT:ABCDEF1234567890123). +If the device has not been paired before, use the code provided by the manufacturer and **ensure the device is in pairing mode**, refer to your devices instructions for pairing for more information. +You can include dashes or omit them in a manual pairing code. + +If the device is paired with another Matter ecosystem (Apple, Google, Amazon, etc..) you must use that ecosystem to generate a new pairing code and search for devices. +The pairing code and device will only be available for commissioning for a limited time. +Refer to the ecosystem that generated the code for the exact duration (typically 5-15 minutes). In this case, openHAB still talks directly to the device and is not associated with that existing ecosystem. + +If the device seems to be found in the logs, but can not be added, its possible the device has been already paired. +Hard resetting the device may help this case. +See your device documentation for how to hard reset the device. + +### Device Pairing: Thread Devices + +Thread devices require a Thread Border Router and a bluetooth enabled device to facilitate the thread joining process (typically a mobile device). +Until there is a supported thread border router integration in openHAB and the openHAB mobile apps, it's strongly recommended to pair the device to a commercial router with thread support first (Apple TV 4k, Google Nest Hub 2, Amazon Gen 4 Echo, etc... ), then generate a matter pairing code using that ecosystem and add the device normally. +This will still allow openHAB to have direct access to the device using only the embedded thread border router and does not interact with the underlying providers home automation stack. + +Support for using a OpenThread Border Router has been verified to work and will be coming soon to openHAB, but in some cases requires strong expertise in IPv6 routing as well as support in our mobile clients. + +### Enabling IPv6 Thread Connectivity on Linux Hosts + +It is important to make sure that Route Announcements (RA) and Route Information Options (RIO) are enabled on your host so that Thread boarder routers can announce routes to the Thread network. +This is done by setting the following sysctl options: + +1. `net.ipv6.conf.wlan0.accept_ra` should be at least `1` if ip forwarding is not enabled, and `2` otherwise. +1. `net.ipv6.conf.wlan0.accept_ra_rt_info_max_plen` should not be smaller than `64`. + +the `accept_ra` is defaulted to `1` for most distributions. + +There may be other network daemons which will override this option (for example, dhcpcd on Raspberry Pi will override accept_ra to 0). + +You can check the accept_ra value with: + +```shell +$ sudo sysctl -n net.ipv6.conf.wlan0.accept_ra +0 +``` + +And set the value to 1 (or 2 in case IP forwarding is enabled) with: + +```shell +$ sudo sysctl -w net.ipv6.conf.wlan0.accept_ra=1 +Net.ipv6.conf.wlan0.accept_ra = 1 +``` + +The accept_ra_rt_info_max_plen option on most Linux distributions is default to 0, set it to 64 with: + +```shell +$ sudo sysctl -w net.ipv6.conf.wlan0.accept_ra_rt_info_max_plen=64 +net.ipv6.conf.wlan0.accept_ra_rt_info_max_plen = 64 +``` + +To make these changes permanent, add the following lines to `/etc/sysctl.conf`: + +```ini +net.ipv6.conf.eth0.accept_ra=1 +net.ipv6.conf.eth0.accept_ra_rt_info_max_plen=64 +``` + +Raspberry Pi users may need to add the following lines to `/etc/dhcpcd.conf` to prevent dhcpcd from overriding the accept_ra value: + +```ini +noipv6 +noipv6rs +``` + +***NOTE: Please ensure you use the right interface name for your network interface.*** The above examples use `wlan0` and `eth0` as examples. +You can find the correct interface name by running `ip a` and looking for the interface that has an IPv6 address assigned to it. + +## Thing Configuration + +### Controller Thing Configuration + +The controller thing must be created manually before devices can be discovered. + +| Name | Type | Description | Default | Required | Advanced | +|--------|--------|----------------------------------------|---------|----------|----------| +| nodeId | number | The matter node ID for this controller | 0 | yes | no | + +Note: The controller nodeId must not be changed after a controller is created. + +### Node Thing Configuration + +Nodes are discovered automatically (see [Discovery](#Discovery) for more information) and should not be added manually. + +| Name | Type | Description | Default | Required | Advanced | +|------------|--------|------------------------------------|---------|----------|----------| +| nodeId | text | The node ID of the endpoint | N/A | yes | no | + +### Endpoint Thing Configuration + + Endpoints are discovered automatically once their parent Node has been added (see [Discovery](#Discovery) for more information) and should not be added manually. + +| Name | Type | Description | Default | Required | Advanced | +|------------|--------|------------------------------------|---------|----------|----------| +| endpointId | number | The endpoint ID within the node | N/A | yes | no | + +## Thing Actions + +### Node Thing Actions + +| Name | Description | +|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Decommission Matter node from fabric | This will remove the device from the Matter fabric. If the device is online and reachable this will attempt to remove the credentials from the device first before removing it from the network. Once a device is removed, this Thing will go offline and can be removed. | +| Generate a new pairing code for a Matter device | Generates a new manual and QR pairing code to be used to pair the Matter device with an external Matter controller | +| List Connected Matter Fabrics | This will list all the Matter fabrics this node belongs to | +| Remove Connected Matter Fabric | This removes a connected Matter fabric from a device. Use the 'List connected Matter fabrics' action to retrieve the fabric index number | + + +For nodes that contain a Thread Border Router Management Cluster, the following additional actions will be present + +| Name | Description | +|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Thread: Load external operational dataset | Updates the local operational dataset configuration from a hex or JSON string for the node. Use the 'Push local operational dataset' action to push the dataset back to the device after loading. | +| Thread: Load operational dataset from device | Updates the local operational dataset configuration from the device. | +| Thread: Operational Dataset Generator | Generates a new operational dataset and optionally saves it locally. | +| Thread: Push local operational dataset | Pushes the local operational dataset configuration to the device. | + +A Thread operational data set is a hex encoded string which contains a Thread border router's configuration. +Using the same operational data set across multiple Thread border routers allows those routers to form a single network where Thread devices can roam from router to router. +Some Thread border routers allow a "pending" operational dataset to be configured, this allows routers to coordinate the configuration change with current Thread devices without requiring those devices to be reconfigured (live migration). + +## Channels + +### Controller Channels + +Controllers have no channels. + +### Node and Bridge Endpoint Channels + +Channels are dynamically added based on the endpoint type and matter cluster supported. +Each endpoint is represented as a channel group. +Possible channels include: + +## Endpoint Channels + +| Channel ID | Type | Label | Description | Category | ReadOnly | Pattern | +|-------------------------------------------------------------|--------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------|----------|-------------| +| battery-voltage | Number:ElectricPotential | Battery Voltage | The current battery voltage | Energy | true | %.1f %unit% | +| battery-alarm | String | Battery Alarm | The battery alarm state | Energy | true | | +| powersource-batpercentremaining | Number:Dimensionless | Battery Percent Remaining | Indicates the estimated percentage of battery charge remaining until the battery will no longer be able to provide power to the Node | Energy | true | %d %% | +| powersource-batchargelevel | Number | Battery Charge Level | Indicates a coarse ranking of the charge level of the battery, used to indicate when intervention is required | Energy | true | | +| booleanstate-statevalue | Switch | Boolean State | Indicates a boolean state value | Status | true | | +| colorcontrol-color | Color | Color | The color channel allows to control the color of a light. It is also possible to dim values and switch the light on and off. | ColorLight | | | +| colorcontrol-temperature | Dimmer | Color Temperature | Sets the color temperature of the light | ColorLight | | | +| colorcontrol-temperature-abs | Number:Temperature | Color Temperature | Sets the color temperature of the light in mirek | ColorLight | | %.0f %unit% | +| doorlock-lockstate | Switch | Door Lock State | Locks and unlocks the door and maintains the lock state | Door | | | +| fancontrol-fanmode | Number | Fan Mode | Set the fan mode | HVAC | | | +| onoffcontrol-onoff | Switch | Switch | Switches the power on and off | Light | | | +| levelcontrol-level | Dimmer | Dimmer | Sets the level of the light | Light | | | +| modeselect-mode | Number | Mode Select | Selection of 1 or more states | | | %d | +| switch-switch | Number | Switch | Indication of a switch or remote being activated | | true | %d | +| switch-switchlatched | Trigger | Switched Latched Trigger | This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. after the move. | | | | +| switch-initialpress | Trigger | Initial Press Trigger | This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. while pressed. | | | | +| switch-longpress | Trigger | Long Press Trigger | This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. while pressed. | | | | +| switch-shortrelease | Trigger | Short Release Trigger | This trigger shall indicate the previous value of the CurrentPosition attribute as a JSON object, i.e. just prior to release. | | | | +| switch-longrelease | Trigger | Long Release Trigger | This trigger shall indicate the previous value of the CurrentPosition attribute as a JSON object, i.e. just prior to release. | | | | +| switch-multipressongoing | Trigger | Multi-Press Ongoing Trigger | This trigger shall indicate 2 numeric fields as a JSON object. The first is the new value of the CurrentPosition attribute, i.e. while pressed. The second is the multi press code with a value of N when the Nth press of a multi-press sequence has been detected. | | | | +| switch-multipresscomplete | Trigger | Multi-Press Complete Trigger | This trigger shall indicate 2 numeric fields as a JSON object. The first is the new value of the CurrentPosition attribute, i.e. while pressed. The second is how many times the momentary switch has been pressed in a multi-press sequence. | | | | +| thermostat-localtemperature | Number:Temperature | Local Temperature | Indicates the local temperature provided by the thermostat | HVAC | true | %.1f %unit% | +| thermostat-outdoortemperature | Number:Temperature | Outdoor Temperature | Indicates the outdoor temperature provided by the thermostat | HVAC | true | %.1f %unit% | +| thermostat-occupiedheating | Number:Temperature | Occupied Heating Setpoint | Set the heating temperature when the room is occupied | HVAC | | %.1f %unit% | +| thermostat-occupiedcooling | Number:Temperature | Occupied Cooling Setpoint | Set the cooling temperature when the room is occupied | HVAC | | %.1f %unit% | +| thermostat-unoccupiedheating | Number:Temperature | Unoccupied Heating Setpoint | Set the heating temperature when the room is unoccupied | HVAC | | %.1f %unit% | +| thermostat-unoccupiedcooling | Number:Temperature | Unoccupied Cooling Setpoint | Set the cooling temperature when the room is unoccupied | HVAC | | %.1f %unit% | +| thermostat-systemmode | Number | System Mode | Set the system mode of the thermostat | HVAC | | | +| thermostat-runningmode | Number | Running Mode | The running mode of the thermostat | HVAC | true | | +| windowcovering-lift | Rollershutter | Window Covering Lift | Sets the window covering level - supporting open/close and up/down type commands | Blinds | | %.0f %% | +| fancontrol-percent | Dimmer | Fan Control Percent | The current fan speed percentage level | HVAC | true | %.0f %% | +| fancontrol-mode | Number | Fan Control Mode | The current mode of the fan | HVAC | | | +| temperaturemeasurement-measuredvalue | Number:Temperature | Temperature | The measured temperature | Temperature | true | %.1f %unit% | +| occupancysensing-occupied | Switch | Occupancy | Indicates if an occupancy sensor is triggered | Presence | true | | +| relativehumiditymeasurement-measuredvalue | Number:Dimensionless | Humidity | The measured humidity | Humidity | true | %.0f %% | +| illuminancemeasurement-measuredvalue | Number:Illuminance | Illuminance | The measured illuminance in Lux | Illuminance | true | %d %unit% | +| wifinetworkdiagnostics-rssi | Number:Power | Signal | Wi-Fi signal strength indicator. | QualityOfService | true | %d %unit% | +| electricalpowermeasurement-activepower | Number:Power | Active Power | The active power measurement in watts | Energy | true | %.1f %unit% | +| electricalpowermeasurement-activecurrent | Number:ElectricCurrent | Active Current | The active current measurement in amperes | Energy | true | %.1f %unit% | +| electricalpowermeasurement-voltage | Number:ElectricPotential | Voltage | The voltage measurement in volts | Energy | true | %.2f %unit% | +| electricalenergymeasurement-energymeasurmement-energy | Number:Energy | Energy | The measured energy | Energy | true | %.1f %unit% | +| electricalenergymeasurement-cumulativeenergyimported-energy | Number:Energy | Cumulative Energy Imported | The cumulative energy imported measurement | Energy | true | %.1f %unit% | +| electricalenergymeasurement-cumulativeenergyexported-energy | Number:Energy | Cumulative Energy Exported | The cumulative energy exported measurement | Energy | true | %.1f %unit% | +| electricalenergymeasurement-periodicenergyimported-energy | Number:Energy | Periodic Energy Imported | The periodic energy imported measurement | Energy | true | %.1f %unit% | +| electricalenergymeasurement-periodicenergyexported-energy | Number:Energy | Periodic Energy Exported | The periodic energy exported measurement | Energy | true | %.1f %unit% | +| threadnetworkdiagnostics-channel | Number | Channel | The Thread network channel | Network | true | %d | +| threadnetworkdiagnostics-routingrole | Number | Routing Role | The Thread routing role (0=Unspecified, 1=Unassigned, 2=Sleepy End Device, 3=End Device, 4=Reed, 5=Router, 6=Leader) | Network | true | %d | +| threadnetworkdiagnostics-networkname | String | Network Name | The Thread network name | Network | true | | +| threadnetworkdiagnostics-panid | Number | PAN ID | The Thread network PAN ID | Network | true | %d | +| threadnetworkdiagnostics-extendedpanid | Number | Extended PAN ID | The Thread network extended PAN ID | Network | true | %d | +| threadnetworkdiagnostics-rloc16 | Number | RLOC16 | The Thread network RLOC16 address | Network | true | %d | +| threadborderroutermanagement-borderroutername | String | Border Router Name | The name of the Thread border router | Network | true | | +| threadborderroutermanagement-borderagentid | String | Border Agent ID | The unique identifier of the Thread border agent | Network | true | | +| threadborderroutermanagement-threadversion | Number | Thread Version | The version of Thread protocol being used | Network | true | %d | +| threadborderroutermanagement-interfaceenabled | Switch | Interface Enabled | Whether the Thread border router interface is enabled | Network | | | +| threadborderroutermanagement-activedatasettimestamp | Number | Active Dataset Timestamp | Timestamp of the active Thread network dataset | Network | true | %d | +| threadborderroutermanagement-activedataset | String | Active Dataset | The active Thread network dataset configuration | Network | | | +| threadborderroutermanagement-pendingdatasettimestamp | Number | Pending Dataset Timestamp | Timestamp of the pending Thread network dataset (only available if PAN change feature is supported) | Network | true | %d | +| threadborderroutermanagement-pendingdataset | String | Pending Dataset | The pending Thread network dataset configuration (only available if PAN change feature is supported) | Network | | | + +## Full Example + +### Thing Configuration + +```java +Thing configuration example for the Matter controller: +Thing matter:controller:main [ nodeId="1" ] + +Thing configuration example for a Matter node: +Thing matter:node:main:12345678901234567890 [ nodeId="12345678901234567890"] + +Thing configuration example for a Matter bridge endpoint: +Thing matter:endpoint:main:12345678901234567890:2 [ endpointId=2] +``` + +### Item Configuration + +```java +Dimmer MyDimmer "My Endpoint Dimmer" { channel="matter:node:main:12345678901234567890:1#levelcontrol-level" } +Dimmer MyBridgedDimmer "My Bridged Dimmer" { channel="matter:endpoint:main:12345678901234567890:2#levelcontrol-level" } + +``` + +### Sitemap Configuration + +```perl +Optional Sitemap configuration: +sitemap home label="Home" +{ + Frame label="Matter Devices" + { + Dimmer item=MyEndpointDimmer + } +} +``` + +# Matter Bridge + +openHAB can also expose Items and Item groups as Matter devices to 3rd party Matter clients like Google Home, Apple Home and Amazon Alexa. This allows local control for those ecosystems and can be used instead of cloud based integrations for features like voice assistants. + +## Configuration + +The openHAB matter bridge uses Metadata tags with the key "matter", similar to the Alexa, Google Assistant and Apple Homekit integrations. +Matter Metadata tag values generally follow the Matter "Device Type" and "Cluster" specification as much as possible. +Items and item groups are initially tagged with a Matter "Device Type", which are Matter designations for common device types like lights, thermostats, locks, window coverings, etc... +For single items, like a light switch or dimmer, simply tagging the item with the Matter device type is enough. +For more complicated devices, like thermostats, A group item is tagged with the device type, and its child members are tagged with the cluster attribute(s) that it will be associated with. +Multiple attributes use a comma delimited format like `attribute1, attribute2, ... attributeN`. +For devices like fans that support groups with multiple items, but you are only using one item to control (like On/Off or Speed), you can tag the regular item with both the device type and the cluster attribute(s) separated by a comma. + +Pairing codes and other options can be found in the MainUI under "Settings -> Add-on Settings -> Matter Binding" + +### Device Types + +| Type | Item Type | Tag | Option | +|---------------------|---------------------------------------|-------------------|---------------------------------------------------------------------------------| +| OnOff Light | Switch, Dimmer | OnOffLight | | +| Dimmable Light | Dimmer | DimmableLight | | +| Color Light | Color | ColorLight | | +| On/Off Plug In Unit | Switch, Dimmer | OnOffPlugInUnit | | +| Thermostat | Group | Thermostat | | +| Window Covering | Rollershutter, Dimmer, String, Switch | WindowCovering | String types: [OPEN="OPEN", CLOSED="CLOSED"], Switch types: [invert=true/false] | +| Temperature Sensor | Number | TemperatureSensor | | +| Humidity Sensor | Number | HumiditySensor | | +| Occupancy Sensor | Switch, Contact | OccupancySensor | | +| Contact Sensor | Switch, Contact | ContactSensor | | +| Door Lock | Switch | DoorLock | | +| Fan | Group, Switch, String, Dimmer | Fan | | + +### Global Options + +* Endpoint Labels + * By default, the Item label is used as the Matter label but can be overridden by adding a `label` key as a metadata option, either by itself or part of other options required for a device. + * Example: `[label="My Custom Label"]` +* Fixed Labels + * Matter has a concept of "Fixed Labels" which allows devices to expose arbitrary label names and values which can be used by clients for tasks like grouping devices in rooms. + * Example: `[fixedLabels="room=Office, floor=1"]` + +### Thermostat group member tags + +| Type | Item Type | Tag | Options | +|---------------------|------------------------|------------------------------------|------------------------------------------------------------------------------------------| +| Current Temperature | Number | thermostat.localTemperature | | +| Outdoor Temperature | Number | thermostat.outdoorTemperature | | +| Heating Setpoint | Number | thermostat.occupiedHeatingSetpoint | | +| Cooling Setpoint | Number | thermostat.occupiedCoolingSetpoint | | +| System Mode | Number, String, Switch | thermostat.systemMode | [OFF=0,AUTO=1,ON=1,COOL=3,HEAT=4,EMERGENCY_HEAT=5,PRECOOLING=6,FAN_ONLY=7,DRY=8,SLEEP=9] | +| Running Mode | Number, String | thermostat.runningMode | | + +For `systemMode` the `ON` option should map to the system mode custom value that would be appropriate if a 'ON' command was issued, defaults to the `AUTO` mapping. + +The following attributes can be set in the options of any thermostat member or on the Group item to set temperature options. + +| Setting | Description | Value (in 0.01°C) | +|--------------------------------------|-------------------------------------------------------------------------------------------------|-------------------| +| `thermostat-minHeatSetpointLimit` | The minimum allowable heat setpoint limit. | 0 | +| `thermostat-maxHeatSetpointLimit` | The maximum allowable heat setpoint limit. | 3500 | +| `thermostat-absMinHeatSetpointLimit` | The absolute minimum heat setpoint limit that cannot be exceeded by the `minHeatSetpointLimit`. | 0 | +| `thermostat-absMaxHeatSetpointLimit` | The absolute maximum heat setpoint limit that cannot be exceeded by the `maxHeatSetpointLimit`. | 3500 | +| `thermostat-minCoolSetpointLimit` | The minimum allowable cool setpoint limit. | 0 | +| `thermostat-maxCoolSetpointLimit` | The maximum allowable cool setpoint limit. | 3500 | +| `thermostat-absMinCoolSetpointLimit` | The absolute minimum cool setpoint limit that cannot be exceeded by the `minCoolSetpointLimit`. | 0 | +| `thermostat-absMaxCoolSetpointLimit` | The absolute maximum cool setpoint limit that cannot be exceeded by the `maxCoolSetpointLimit`. | 3500 | +| `thermostat-minSetpointDeadBand` | The minimum deadband (temperature gap) between heating and cooling setpoints. | 0 | + +### Fan group member tags + +| Type | Item Type | Tag | Options | +|----------------|------------------------|---------------------------|---------------------------------------------------------| +| Fan Mode | Number, String, Switch | fanControl.fanMode | [OFF=0, LOW=1, MEDIUM=2, HIGH=3, ON=4, AUTO=5, SMART=6] | +| Fan Percentage | Dimmer | fanControl.percentSetting | | +| Fan OnOff | Switch | onOff.onOff | | + +The following attributes can be set on the Fan Mode item or the Group item to set fan options. + +| Setting | Description | Value | +|------------------------------|----------------------------------------------------------------------------------------------------------|-------| +| `fanControl-fanModeSequence` | The sequence of fan modes to cycle through. See [Fan Mode Sequence Options](#fan-mode-sequence-options) | 5 | + +#### Fan Mode Sequence Options + +| Value | Description | +|-------|-------------------| +| 0 | OffLowMedHigh | +| 1 | OffLowHigh | +| 2 | OffLowMedHighAuto | +| 3 | OffLowHighAuto | +| 4 | OffHighAuto | +| 5 | OffHigh | + +### Example + +```java +Dimmer TestDimmer "Test Dimmer [%d%%]" {matter="DimmableLight" [label="My Custom Dimmer", fixedLabels="room=Bedroom 1, floor=2, direction=up, customLabel=Custom Value"]} + +Group TestHVAC "Thermostat" ["HVAC"] {matter="Thermostat" [thermostat-minHeatSetpointLimit=0, thermostat-maxHeatSetpointLimit=3500]} +Number:Temperature TestHVAC_Temperature "Temperature [%d °F]" (TestHVAC) ["Measurement","Temperature"] {matter="thermostat.localTemperature"} +Number:Temperature TestHVAC_HeatSetpoint "Heat Setpoint [%d °F]" (TestHVAC) ["Setpoint", "Temperature"] {matter="thermostat.occupiedHeatingSetpoint"} +Number:Temperature TestHVAC_CoolSetpoint "Cool Setpoint [%d °F]" (TestHVAC) ["Setpoint", "Temperature"] {matter="thermostat.occupiedCoolingSetpoint"} +Number TestHVAC_Mode "Mode [%s]" (TestHVAC) ["Control" ] {matter="thermostat.systemMode" [OFF=0, HEAT=1, COOL=2, AUTO=3]} + +Switch TestDoorLock "Door Lock" {matter="DoorLock"} +Rollershutter TestShade "Window Shade" {matter="WindowCovering"} +Number:Temperature TestTemperatureSensor "Temperature Sensor" {matter="TemperatureSensor"} +Number TestHumiditySensor "Humidity Sensor" {matter="HumiditySensor"} +Switch TestOccupancySensor "Occupancy Sensor" {matter="OccupancySensor"} + +### Fan with group item control +Group TestFan "Test Fan" {matter="Fan" [fanControl-fanModeSequence=3]} +Dimmer TestFanSpeed "Speed" (TestFan) {matter="fanControl.percentSetting"} +Switch TestFanOnOff "On/Off" (TestFan) {matter="fanControl.fanMode"} +Number TestFanMode "Mode" (TestFan) {matter="fanControl.fanMode" [OFF=0, LOW=1, MEDIUM=2, HIGH=3, ON=4, AUTO=5, SMART=6]} + +### Fan with single item control , so no group item is needed +Switch TestFanSingleItem "On/Off" {matter="Fan, fanControl.fanMode"} +``` + +### Bridge FAQ + +* Alexa: When pairing, after a minute Alexa reports "Something went wrong" + * Alexa can take 3-4 seconds per device to process which can take longer then the Alexa UI is willing to wait. + Eventually the pairing will complete, which for a large number of devices may be a few minutes. +* Alexa: Suddenly stops working and says it could not connect to a device or device not responding. + * Check the Settings page in the Main UI to confirm the bridge is running + * Ensure the openHAB item has the proper matter tag, or that the item is being loaded at all (check item file errors) + * Rarely, you may need to reboot the Alexa device. + If you have multiple devices and not sure which is the primary matter connection, you may need to reboot all of them. + +# Matter Ecosystem Overview + +Matter is an open-source connectivity standard for smart home devices, allowing seamless communication between a wide range of devices, controllers, and ecosystems. + +Below is a high-level overview of the Matter ecosystem as well as common terminology used in the Matter standard. + +## Matter Devices + +### Nodes and Endpoints + +In the Matter ecosystem, a **node** represents a single device that joins a Matter network and will have a locally routable IPv6 address. +A **node** can have multiple **endpoints**, which are logical representations of specific features or functionalities of the device. +For example, a smart thermostat (node) may have an endpoint for general thermostat control (heating, cooling, current temperature, operating state, etc....) and another endpoint for humidity sensing. +Many devices will only have a single endpoint. +[Matter Bridges](#bridges) will expose multiple endpoints for each device they are bridging, and the bridge itself will be a node. + +**Example:** + +- A Thermostat node with an endpoint for general temperature control and another endpoint for a remote temperature or humidity sensor. + +### Controllers + +A **controller** manages the interaction between Matter devices and other parts of the network. +Controllers can send commands, receive updates, and facilitate device communication. +They also handle the commissioning process when new devices are added to the network. + +**Example:** + +- openHAB or another smart home hub or a smartphone app that manages your smart light bulbs, door locks, and sensors (Google Home, Apple Home, Amazon Alexa, etc...) + +### Bridges + +A **bridge** is a special type of node that connects non-Matter devices to a Matter network, effectively translating between protocols. +Bridges allow legacy devices to be controlled via the Matter standard. + +openHAB fully supports connecting to Matter bridges. +In addition, openHAB has support for running its own Matter bridge service, exposing openHAB items as Matter endpoints to 3rd party systems. +See [Matter Bridge](#Matter-Bridge) for information on running a Bridge server. + +**Example:** + +- A bridge that connects Zigbee or Z-Wave devices, making them accessible within a Matter ecosystem. The Ikea Dirigera and Philips Hue Bridge both act as matter bridges and are supported in openHAB. + +### Thread Border Routers + +A **Thread Border Router** is a device that allows devices connected via Thread (a low-power wireless protocol) to communicate with devices on other networks, such as Wi-Fi or Ethernet. +It facilitates IPv6-based communication between Thread networks and the local IP network. + +**Example:** + +- An OpenThread Boarder Router (open source) as well as recent versions of Apple TVs, Amazon Echos and Google Nest Hubs all have embedded thread boarder routers. + +## IPv6 and Network Connectivity + +Matter devices operate over an IPv6 network, and obtaining an IPv6 address is required for communication. +Devices can connect to the network via different interfaces: + +### Ethernet + +Ethernet-connected Matter devices receive an IPv6 address through standard DHCPv6 or stateless address auto-configuration (SLAAC). + +### Wi-Fi + +Wi-Fi-enabled Matter devices also receive an IPv6 address using DHCPv6 or SLAAC. +They rely on the existing Wi-Fi infrastructure for communication within the Matter ecosystem. + +### Thread + +Thread-based Matter devices connect to the network via a **Thread Border Router**. +They receive an IPv6 address from the Thread router. + +## IPv6 Requirements + +For Matter devices to function correctly, **IPv6 must be enabled** and supported in both the local network (router) and the Matter controllers. +Without IPv6, devices won't be able to communicate properly within the Matter ecosystem. +Ensure that your router has IPv6 enabled and that any Matter controllers (like smart hubs, apps or openHAB) are configured to support IPv6 as well. + +**Note that environments like Docker require special configurations to enable IPv6** + +## Matter Commissioning and Pairing Codes + +Commissioning a Matter device involves securely adding it to the network using a **pairing code**. +This process ensures that only authorized devices can join the network. + +### Pairing Code from the Device + +When commissioning a new Matter device, it typically has a printed QR code or numeric pairing code that you scan or enter during setup. This pairing code allows the controller to establish a secure connection to the device and add it to the network. +Once a device pairing code is in use, it typically can not be used again to pair other controllers. + +### Additional Pairing Code from a Controller + +If a device has already been commissioned and you want to add it to another Matter controller, the existing controller can generate an additional pairing code. +This is useful when sharing access to a device across multiple hubs or apps. +Apple Home, Google Home, Amazon Alexa and openHAB all support generating pairing codes for existing paired devices. + +### Example: + +- When setting up a smart lock, you may scan a QR code directly from the lock, or use the 11 digit pairing code printed on it to pair it with openHAB. If you later want to control the lock from another app or hub, you would retrieve a new pairing code directly from openHAB. diff --git a/bundles/org.openhab.binding.matter/code-gen/README.md b/bundles/org.openhab.binding.matter/code-gen/README.md new file mode 100644 index 00000000000..50f05b7dcc6 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/README.md @@ -0,0 +1,82 @@ +# Matter Code Generator + +This system generates Java classes for Matter clusters, device types, and related functionality. It uses Handlebars templates to transform Matter.js protocol definitions into Java code suitable for serialization. + +## Overview + +The code generator consists of: + +- `app.ts`: Main generator script that processes Matter.js definitions and generates Java code +- Template files in `src/templates/`: + - `cluster-class.hbs`: Template for individual cluster classes + - `base-cluster.hbs`: Template for the base cluster class + - `cluster-constants.hbs`: Template for cluster constants + - `cluster-registry.hbs`: Template for cluster registry + - `device-types-class.hbs`: Template for device type definitions + - `data-types-class.hbs`: Template for data type definitions + +## Main Generator (app.ts) + +The generator script: + +1. Imports Matter.js protocol definitions +2. Maps Matter.js data types to Java types +3. Processes cluster inheritance and references between clusters +4. Compiles Handlebars templates +5. Generates Java code files in the `out/` directory + + +## Templates + +### cluster-class.hbs +Generates individual cluster classes with: +- Cluster attributes +- Struct definitions +- Enum definitions +- Command methods +- toString() implementation + +### base-cluster.hbs +Generates the base cluster class with: +- Common fields and methods +- Global struct/enum definitions + +### cluster-constants.hbs +Generates constants for: +- Channel names +- Channel labels +- Channel IDs +- Channel type UIDs + +note this is not currently used yet in the binding + +### cluster-registry.hbs +Generates a registry mapping cluster IDs to cluster classes + +### device-types-class.hbs +Generates device type definitions and mappings + +## Usage + +1. Install dependencies: +2. Run the generator: +3. Generated Java files will be in the `out/` directory + +```bash +npm install && npm run start +``` + +Note the the maven pom.xml will execute these steps when building the project, including linting the generated files and moving them from the out directory to the primary addon src directory. + +## Handlebars Helpers + +The generator includes several Handlebars helpers for string manipulation to assist in Java naming conventions: + +- `asUpperCase`: Convert to uppercase +- `asLowerCase`: Convert to lowercase +- `asUpperCamelCase`: Convert to UpperCamelCase +- `asLowerCamelCase`: Convert to lowerCamelCase +- `asTitleCase`: Convert to Title Case +- `asEnumField`: Convert to ENUM_FIELD format +- `asHex`: Convert number to hex string +- and many others diff --git a/bundles/org.openhab.binding.matter/code-gen/package-lock.json b/bundles/org.openhab.binding.matter/code-gen/package-lock.json new file mode 100644 index 00000000000..9bcb26008df --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/package-lock.json @@ -0,0 +1,1556 @@ +{ + "name": "code-gen", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "code-gen", + "version": "0.1.0", + "dependencies": { + "@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "handlebars": "^4.7.8" + }, + "devDependencies": { + "ts-loader": "^9.4.4", + "ts-node": "^10.9.2", + "typescript": "^5.2.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@matter/general": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/general/-/general-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-0rkdLhn/ETSW5w/MQqJQnYRPtOwFx2VKRHAiu5w1lHS7TIVtJ4emPLq2HMSiQ2HMAEDz9eYzfIO4OueEhCbW4A==", + "dependencies": { + "@noble/curves": "^1.9.1" + } + }, + "node_modules/@matter/main": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/main/-/main-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-tu/XAD3wK0kzI9PfXHUK5T0z4Y2vNUMZhbxNe7kSB4kHHjWCHitv1NMG0/uEIWRkSrm4/e82WCdJUrWxLgtm0g==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + }, + "optionalDependencies": { + "@matter/nodejs": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/model": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/model/-/model-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-NvXbecY1WUjXVuXSzZX78uP50rixPNKM8mKP6JRzUpO8sKys1JwZIiJl3kGKPdTuvwot2hwpFJURtnReDam/MA==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/node": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/node/-/node-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-dJt1HoFeomJKfBwcEkK2iAETLJfUVebYzCZ9Ie2qJ37uNc7DfhRomy/Dvj+deHS2Icfalm/COrkMiJQf2G3SvA==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/nodejs": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-TTI7HfubBUQEZjcQAmcikoEerTfEt5iCy7ctSwzvt+M0PMToLyj3UrOgDEezezYw+hPzTBmitLWyb/VBnSpk0g==", + "optional": true, + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@matter/protocol": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-y3PNdjtuiA1mAHnfpA3U30y29zQsxW3BOB3anYmGnpKTLTQia6hpmtp3FGHMBMi8rMNW5+PgQT9Gx2f/G+WwFg==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/types": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/types/-/types-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-72AgxSd1PQ5jxILMqt3JJ0Eg+duBekM/Bbjy7RIsXAB6eKPwXvvGypC4jjHn/FrAFbS7s0UVIvezT+xvxQM1gg==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/node": { + "version": "22.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz", + "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001710", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001710.tgz", + "integrity": "sha512-B5C0I0UmaGqHgo5FuqJ7hBd4L57A4dDD+Xi+XX1nXOoxGeDdY4Ko38qJYOyqznBVJEqON5p8P1x5zRR3+rsnxA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.132", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz", + "integrity": "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "peer": true + }, + "node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/bundles/org.openhab.binding.matter/code-gen/package.json b/bundles/org.openhab.binding.matter/code-gen/package.json new file mode 100644 index 00000000000..b0251571368 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/package.json @@ -0,0 +1,26 @@ +{ + "name": "code-gen", + "version": "0.1.0", + "description": "", + "scripts": { + "clean": "tsc --clean", + "build": "tsc --build", + "build-clean": "tsc --build --clean", + "start": "ts-node src/app.ts", + "test": "ts-node src/test.ts" + }, + "devDependencies": { + "ts-loader": "^9.4.4", + "ts-node": "^10.9.2", + "typescript": "^5.2.2" + }, + "dependencies": { + "@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "handlebars": "^4.7.8" + }, + "files": [ + "dist/**/*", + "src/**/*", + "README.md" + ] +} diff --git a/bundles/org.openhab.binding.matter/code-gen/src/app.ts b/bundles/org.openhab.binding.matter/code-gen/src/app.ts new file mode 100644 index 00000000000..018be2adbd8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/app.ts @@ -0,0 +1,536 @@ +import { AnyElement, FieldElement, Matter, ClusterElement, DatatypeElement, AttributeElement, CommandElement, AnyValueElement, ClusterModel, MatterModel } from "@matter/main/model"; +import "@matter/model/resources"; +import handlebars from "handlebars"; +import { Bytes } from "@matter/general" +import fs from "fs"; + +// Convert Matter object to JSON string and parse it back, this is a workaround to avoid some typescript issues iterating over the Matter object +const matterData = JSON.parse(JSON.stringify(Matter)) as MatterModel; + +matterData.children.filter(c => c.name == 'LevelControl' || c.name == 'ColorControl').forEach(c => { + c.children?.filter(c => c.tag == 'command').forEach(c => { + c.children?.filter(c => c.type == 'Options').forEach(c => { + c.type = 'OptionsBitmap'; + }) + }) +}) + +interface ExtendedClusterElement extends ClusterElement { + attributes: AnyValueElement[], + commands: CommandElement[], + datatypes: AnyValueElement[], + enums: AnyValueElement[], + structs: AnyValueElement[], + maps: AnyValueElement[], + typeMapping: Map +} + +function toJSON(data: any, space = 2) { + return JSON.stringify(data, (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + if (value instanceof Uint8Array) { + return Bytes.toHex(value); + } + if (value === undefined) { + return "undefined"; + } + return value; + }, space); +} + +handlebars.registerHelper('asUpperCase', function (str) { + return toUpperCase(str); +}); + +handlebars.registerHelper('asLowerCase', function (str) { + return toLowerCase(str); +}); + +handlebars.registerHelper('asUpperCamelCase', function (str) { + return toUpperCamelCase(str); +}); + +handlebars.registerHelper('asLowerCamelCase', function (str) { + return toLowerCamelCase(str); +}); + +handlebars.registerHelper('asTitleCase', function (str) { + return toTitleCase(str); +}); + +handlebars.registerHelper('asEnumField', function (str) { + return toEnumField(str); +}); + +handlebars.registerHelper('asUpperSnakeCase', function (str) { + return toUpperSnakeCase(str); +}); + +handlebars.registerHelper('asSpacedTitleCase', function (str) { + return toSpacedTitleCase(str); +}); + +handlebars.registerHelper('asHex', function (decimal, length) { + return toHex(decimal, length); +}); + +handlebars.registerHelper('isLastElement', function (index: number, count: number) { + return index >= count - 1; +}); +handlebars.registerHelper('isFirstElement', function (index: number) { + return index == 0; +}); +handlebars.registerHelper('isEmpty', function (e: Array | String | undefined) { + return e == undefined || e.length == 0 +}); +handlebars.registerHelper('isDepreciated', function (field) { + return field.conformance == "D" || field.conformance == "X" || field.conformance == "[!LT]" +}); +handlebars.registerHelper('isReadOnly', function (field) { + return field.access.indexOf('RW') == -1; +}); +handlebars.registerHelper('toBitmapType', function (constraint) { + return constraint != undefined && constraint.indexOf(" to ") > 0 ? "short" : "boolean" +}); +handlebars.registerHelper('toBitmapChildName', function (child, type) { + return type == "FeatureMap" ? toLowerCamelCase(child.title) : toLowerCamelCase(child.name) +}); +handlebars.registerHelper('isAttribute', function (field) { + return field.tag == 'attribute' +}); + +handlebars.registerHelper('isNonNull', function (field) { + return field.access?.indexOf('RW') > -1 || field.isNonNull; +}); + +function toUpperCase(str: string | undefined) { + if (str == undefined) { + return "UNDEFINED" + } + return str.toUpperCase(); +} + +function toLowerCase(str: string | undefined) { + if (str == undefined) { + return "undefined" + } + return str.toLowerCase(); +} + +function toUpperCamelCase(str: string | undefined) { + if (str == undefined) { + return "undefined" + } + return str.replace(/(^\w|[_\s]\w)/g, match => match.replace(/[_\s]/, '').toUpperCase()); +} + +function toLowerCamelCase(str: string): string { + if (str == undefined) { + return "undefined" + } + return str.replace(/(?:^\w|[_\s]\w)/g, (match, offset) => { + return offset === 0 ? match.toLowerCase() : match.replace(/[_\s]/, '').toUpperCase(); + }); +} + +function toTitleCase(str: string | undefined): string { + if (!str) { + return "Undefined"; + } + return str + .replace(/([a-z])([A-Z])/g, '$1 $2') // Add a space before uppercase letters that follow lowercase letters + .replace(/[_\s]+/g, ' ') // Replace underscores or multiple spaces with a single space + .trim() + .split(' ') + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' '); +} + +function toEnumField(str: string): string { + // Check if the string starts with a number and prepend "V" if it does + if (/^\d/.test(str)) { + str = 'V' + str; + } + + // First split camelCase words by inserting underscores + str = str + // Split between lowercase and uppercase letters + .replace(/([a-z])([A-Z])/g, '$1_$2') + // Split between uppercase letters followed by lowercase + .replace(/([A-Z])([A-Z][a-z])/g, '$1_$2') + // Replace any remaining spaces with underscores + .replace(/\s+/g, '_') + // Finally convert to uppercase + .toUpperCase(); + + return str; +} + +function toUpperSnakeCase(str: string | undefined) { + if (str == undefined) { + return "UNDEFINED" + } + return str + .replace(/([a-z])([A-Z])/g, '$1_$2') // Insert underscore between camelCase + .replace(/[\s-]+/g, '_') // Replace spaces and hyphens with underscore + .toUpperCase(); +} + +function toSpacedTitleCase(str: string | undefined): string { + if (!str) { + return "Undefined"; + } + return str + .replace(/([a-z])([A-Z])/g, '$1 $2') // Add a space before uppercase letters that follow lowercase letters + .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2') // Split between capital letters when followed by capital+lowercase + .replace(/([a-z])([A-Z][a-z])/g, '$1 $2') // Split between lowercase and camelCase word + .replace(/([a-zA-Z])(\d)/g, '$1 $2') // Split between letters and numbers + .replace(/(\d)([a-zA-Z])/g, '$1 $2') // Split between numbers and letters + .replace(/[_\s]+/g, ' ') // Replace underscores or multiple spaces with a single space + .trim(); +} + +function toHex(decimal: number, length = 0) { + let hex = decimal.toString(16).toUpperCase(); + if (length > 0) { + hex = hex.padStart(length, '0'); + } + return `0x${hex}`; +} + +/** + * + * @param field Lookup function to map matter native types to Java native types + * @returns + */ +function matterNativeTypeToJavaNativeType(field: AnyElement) { + switch (field.type || field.name) { + case "bool": + return "Boolean"; + case "uint8": + case "uint16": + case "uint24": + case "uint32": + case "int8": + case "int16": + case "int24": + case "int32": + case "status": + return "Integer"; + case "uint40": + case "uint48": + case "uint56": + case "uint64": + case "int40": + case "int48": + case "int56": + case "int64": + return "BigInteger"; + case "single": + return "Float"; + case "double": + return "Double"; + case "date": + return "date"; + case "string": + case "locationdesc": + return "String"; + case "octstr": + return "OctetString"; + // this are semantic tag fields + case "tag": + case "namespace": + return "Integer"; + case "list": + case "struct": + case "map8": + case "map16": + case "map32": + case "map64": + case "map8": + case "map16": + case "map32": + case "map64": + //these are complex types and do not map to a Java native type + default: + return undefined; + } +} + +function filterDep(e: AnyValueElement) { + //remove fields flagged as depreciated or not used + const children = e.children?.filter(field => { + const f = field as FieldElement; + return f.conformance != 'X' && f.conformance != 'D' + }) + e.children = children as AnyValueElement[]; + return e; +} + +/** + * Type mapper attempts to lookup the Java native type for any matter element, this include Integers, Strings, Booleans, etc... + * + * If there is no matching type, then the matter element is a complex type, like maps, enums and structs + * These complex types are represented as Java classes, so the mapping will refer to that complex type which + * will be templated out later in the code + * + * This code also traverses any children of the data type, applying the same logic + * + * @param mappings - existing set of mapping lookups for types + * @param dt - the data type we are operating on + * @returns the data type which now includes a new field 'mappedType' on all levels of the object + */ +function typeMapper(mappings: Map, dt: AnyValueElement): any { + let mappedType: string | undefined; + if (dt.tag == 'attribute' && dt.type?.startsWith('enum') || dt.type?.startsWith('map') || dt.type?.startsWith('struct')) { + //these types will be generated as inner classes and will be referred to by name + mappedType = dt.name; + } else { + //this gets raw types + mappedType = dt.type && mappings.get(dt.type) || matterNativeTypeToJavaNativeType(dt) || dt.type || "String" + } + if (mappedType == 'list') { + const ct = dt.children?.[0].type + //if the type is cluster.type then its referring to type in another cluster + if (ct && ct.indexOf('.') > 0) { + const [otherCluster, otherType] = ct.split('.'); + mappedType = `List<${toUpperCamelCase(otherCluster + "Cluster")}.${toUpperCamelCase(otherType)}>`; + } else { + mappedType = `List<${toUpperCamelCase(ct && mappings.get(ct) || ct)}>`; + } + } else if (mappedType && mappedType.indexOf('.') > 0) { + //some types reference other clusters, like MediaPlayback.CharacteristicEnum + const [cName, dtName] = mappedType.split('.'); + mappedType = `${toUpperCamelCase(cName)}Cluster.${toUpperCamelCase(dtName)}`; + } else if (mappings.get(mappedType)) { + //if the type is already mapped, then use the mapped type + mappedType = mappings.get(mappedType) + } + + const children = dt.children?.map((child) => { + return typeMapper(mappings, child as AnyValueElement) as AnyValueElement + }) + return { + ...dt, + children: children, + mappedType: mappedType + } +} + +/** + * Certain clusters have complex inheritance that we don't support yet (and don't need right now) + */ +const skipClusters = new Set(['Messages']); + +/** + * Global types (not in a cluster) + */ +const globalDataTypes = (matterData.children as DatatypeElement[]).filter(c => c.tag === 'datatype'); +const globalAttributes = (matterData.children as AttributeElement[]).filter(c => c.tag === 'attribute'); + +/** + * Global type mapping lookup, clusters will combine this with their own mapping + */ +const globalTypeMapping = new Map(); +//some types are special and need to be mapped to Java native types here +globalTypeMapping.set("FabricIndex", "Integer"); +// semantic tag fields +globalTypeMapping.set("namespace", "Integer"); +globalTypeMapping.set("tag", "Integer"); + +globalDataTypes.forEach(dt => { + matterNativeTypeToJavaNativeType(dt) && globalTypeMapping.set(dt.name, matterNativeTypeToJavaNativeType(dt)) +}); +//it seems like there is a global data type that overrides the string type +globalTypeMapping.set("string", "String"); + +globalAttributes.forEach(dt => matterNativeTypeToJavaNativeType(dt) && globalTypeMapping.set(dt.name, matterNativeTypeToJavaNativeType(dt))); + +const clusters: ExtendedClusterElement[] = (matterData.children as ClusterElement[]) + .filter(c => c.tag === 'cluster') + .filter(c => !skipClusters.has(c.name)) + .map(cluster => { + // typeMapping is a map of matter types to Java types + const typeMapping = new Map(globalTypeMapping); + const dataTypes = (cluster.children || []).filter(c => c.tag === 'datatype') as DatatypeElement[]; + const maps = (cluster.children || []).filter(c => c.type?.startsWith('map')) as AnyValueElement[]; + const enums = (cluster.children || []).filter(c => c.type?.startsWith('enum')) as AnyValueElement[]; + const structs = (cluster.children || []) + .filter(dt => dt.type === 'struct' || dt.tag === 'event') + .map(dt => typeMapper(typeMapping, dt as AnyValueElement)); + dataTypes?.forEach(dt => { + if (dt.type && dt.type.indexOf('.') > 0) { + return typeMapping.set(dt.name, dt.type) + } + return matterNativeTypeToJavaNativeType(dt) && typeMapping.set(dt.name, matterNativeTypeToJavaNativeType(dt)) + }); + + // if the cluster has a type, then the java class will extend this type (which is another cluster) + const parent = cluster.type ? matterData.children.find(c => c.name == cluster.type) : undefined; + + const attributes = cluster.children?.filter(c => c.tag == 'attribute')?.filter((element, index, self) => { + //remove duplicates, not sure why they exist in the model + const dupIndex = self.findIndex(e => e.name === element.name) + if (dupIndex != index) { + if (element.conformance?.toString().startsWith('[!')) { + return false; + } + } + // if the parent cluster has an attribute with the same name, then don't include it as we need to use the parent's attribute + if (parent) { + const parentAttr = parent.children?.filter(c => c.tag == 'attribute')?.find(c => c.name == element.name) + if (parentAttr) { + return false; + } + } + return true + }).filter(attr => filterDep(attr)).map(dt => typeMapper(typeMapping, dt)) + + //some command types reference attribute types (LevelControl Options) + attributes?.forEach(dt => { + if (dt.type && dt.type.indexOf('.') > 0) { + typeMapping.set(dt.name, dt.type) + return; + } + typeMapping.set(dt.name, matterNativeTypeToJavaNativeType(dt) || dt.type) + + //some local Attributes like FeatureMap reference the global attribute type + if (dt.children) { + const ga = globalAttributes.find(a => a.name == dt.type) + if (ga && ga.type?.startsWith('map') && !maps?.find(e => e.name == dt.name)) { + maps?.push(dt as ClusterModel.Child) + } + } + }); + + + //clean up commands + const commandsRaw = cluster.children?.filter(c => c.tag == 'command'); + const commands = commandsRaw?.map(command => { + //some commands reference others + if (command.type != undefined) { + command.children = commandsRaw?.find(c => c.name == command.type)?.children || [] + } + return command + }).map(command => filterDep(command)).filter(c => (c as CommandElement).direction == "request").map(dt => { + if (dt.type && dt.type.indexOf('.') > 0) { + typeMapping.set(dt.name, dt.type) + return; + } + const newCommand = typeMapper(typeMapping, dt) + newCommand.children?.forEach((c: any) => { + if (c.type?.startsWith('map') && !maps?.find(e => e.name == c.name)) { + maps?.push(c as ClusterModel.Child) + } + }); + return newCommand + }); + + return { + ...cluster, + attributes: attributes, + commands: commands, + datatypes: dataTypes, + enums: enums, + structs: structs, + maps: maps, + typeMapping: typeMapping + } as ExtendedClusterElement +}) || [] + +function copyClusterDatatype(sourceCluster: ExtendedClusterElement, destCluster: ExtendedClusterElement, name: string) { + let dt = sourceCluster.datatypes?.find(d => d.name == name) || sourceCluster.enums?.find(d => d.name == name) || sourceCluster.structs?.find(d => d.name == name) || sourceCluster.attributes?.find(d => d.name == name) + if (dt) { + destCluster.typeMapping.set(name, name); + if (dt.type) { + if (dt.type.startsWith('enum')) { + if (!destCluster.enums) { + destCluster.enums = [] + } + destCluster.enums.push(dt) + } else if (dt.type.startsWith('map')) { + if (!destCluster.maps) { + destCluster.maps = [] + } + destCluster.maps.push(dt) + } else if (dt.type == 'struct') { + if (!destCluster.structs) { + destCluster.structs = [] + } + destCluster.structs.push(dt) + } else { + if (!destCluster.datatypes) { + destCluster.datatypes = [] + } + destCluster.datatypes.push(dt); + } + } + destCluster.commands = destCluster.commands.map(c => typeMapper(destCluster.typeMapping, c)) + + } +} +clusters.forEach(cluster => { + cluster.typeMapping.forEach((value, key, map) => { + if (value && value.indexOf('.') > 0) { + const [cName, dtName] = value.split('.'); + const otherCluster = clusters.find(c => c.name != cluster.name && c.name == cName) + if (otherCluster) { + copyClusterDatatype(otherCluster, cluster, dtName); + } + return; + } + }); +}); + +// Compile Handlebars template +const clusterSource = fs.readFileSync('src/templates/cluster-class.java.hbs', 'utf8'); +const clusterTemplate = handlebars.compile(clusterSource); +const baseClusterSource = fs.readFileSync('src/templates/base-cluster.java.hbs', 'utf8'); +const baseClusterTemplate = handlebars.compile(baseClusterSource); +const deviceTypeSource = fs.readFileSync('src/templates/device-types-class.java.hbs', 'utf8'); +const deviceTypeTemplate = handlebars.compile(deviceTypeSource); +const clusterRegistrySource = fs.readFileSync('src/templates/cluster-registry.java.hbs', 'utf8'); +const clusterRegistryTemplate = handlebars.compile(clusterRegistrySource); +const clusterConstantsSource = fs.readFileSync('src/templates/cluster-constants.java.hbs', 'utf8'); +const clusterConstantsTemplate = handlebars.compile(clusterConstantsSource); + +// Generate Java code + + +const datatypes = { + enums: [ + ...globalDataTypes?.filter(c => c.type?.startsWith('enum')), + ...globalAttributes?.filter(c => c.type?.startsWith('enum')) + ], + structs: [ + ...globalDataTypes?.filter(c => c.type?.startsWith('struct')), + ...globalAttributes?.filter(c => c.type?.startsWith('struct')) + ].map(e => typeMapper(globalTypeMapping, e)), + maps: [ + ...globalDataTypes?.filter(c => c.type?.startsWith('map')), + ...globalAttributes?.filter(c => c.type?.startsWith('map')) + ] +} + +fs.mkdir('out', { recursive: true }, (err) => { +}); + +const baseClusterClass = baseClusterTemplate(datatypes); +fs.writeFileSync(`out/BaseCluster.java`, baseClusterClass); + +const deviceTypeClass = deviceTypeTemplate({ deviceTypes: matterData.children.filter((c: AnyElement) => c.tag === 'deviceType' && c.id !== undefined) }); +fs.writeFileSync(`out/DeviceTypes.java`, deviceTypeClass); + + +const clusterRegistryClass = clusterRegistryTemplate({ clusters: clusters }); +fs.writeFileSync(`out/ClusterRegistry.java`, clusterRegistryClass); + +const clusterConstantsClass = clusterConstantsTemplate({ clusters: clusters }); +fs.writeFileSync(`out/ClusterConstants.java`, clusterConstantsClass); + +clusters.forEach(cluster => { + const javaCode = clusterTemplate(cluster); + fs.writeFileSync(`out/${cluster.name}Cluster.java`, javaCode); +}); \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/code-gen/src/templates/base-cluster.java.hbs b/bundles/org.openhab.binding.matter/code-gen/src/templates/base-cluster.java.hbs new file mode 100644 index 00000000000..705a462e730 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/templates/base-cluster.java.hbs @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; + +import com.google.gson.Gson; + +/** + * {{asUpperCamelCase name}} + * + * @author Dan Cunningham - Initial contribution + */ + + public class BaseCluster { + + protected static final Gson GSON = new Gson(); + public BigInteger nodeId; + public int endpointId; + public int id; + public String name; + + public interface MatterEnum { + Integer getValue(); + + String getLabel(); + + public static E fromValue(Class enumClass, int value) { + E[] constants = enumClass.getEnumConstants(); + if (constants != null) { + for (E enumConstant : constants) { + if (enumConstant != null) { + if (enumConstant.getValue().equals(value)) { + return enumConstant; + } + } + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } + } + + public BaseCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + this.nodeId = nodeId; + this.endpointId = endpointId; + this.id = clusterId; + this.name = clusterName; + } + + public static class OctetString { + public byte[] value; + + public OctetString(byte[] value) { + this.value = value; + } + + public OctetString(String hexString) { + int length = hexString.length(); + value = new byte[length / 2]; + for (int i = 0; i < length; i += 2) { + value[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + + Character.digit(hexString.charAt(i + 1), 16)); + } + } + + public @NonNull String toHexString() { + StringBuilder hexString = new StringBuilder(); + for (byte b : value) { + String hex = Integer.toHexString(0xFF & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + public @NonNull String toString() { + return toHexString(); + } + } + +{{#each structs}} +{{#if (isFirstElement @index)}} + //Structs +{{/if}} + public class {{asUpperCamelCase name}} { +{{#each children}} + public {{{asUpperCamelCase mappedType}}} {{asLowerCamelCase name}}; // {{type}} +{{/each}} + public {{asUpperCamelCase name}}({{#each children}}{{{asUpperCamelCase mappedType}}} {{asLowerCamelCase name}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}) { +{{#each children}} + this.{{asLowerCamelCase name}} = {{asLowerCamelCase name}}; +{{/each}} + } + } +{{/each}} + + +{{#each enums}} +{{#if (isFirstElement @index)}} + //Enums +{{/if}} + public enum {{asUpperCamelCase name}} implements MatterEnum { +{{#if (isEmpty children)}} + DEFAULT(0, "Default"); +{{else}} +{{#each children}} + {{asEnumField name}}({{id}}, "{{name}}"){{#unless (isLastElement @index ../children.length)}},{{else}};{{/unless}} +{{/each}} +{{/if}} + public final Integer value; + public final String label; + private {{asUpperCamelCase name}}(Integer value, String label){ + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + +{{/each}} + +{{#each maps}} +{{#if (isFirstElement @index)}} + // Bitmaps +{{/if}} + public static class {{asUpperCamelCase name}} { +{{#if (isEmpty children)}} + public List map; + public {{asUpperCamelCase name}}(List map){ + this.map = map; +{{else}} +{{#each children}} + public boolean {{asLowerCamelCase name}}; +{{/each}} + public {{asUpperCamelCase name}}({{#each children}}boolean {{asLowerCamelCase name}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}){ +{{#each children}} + this.{{asLowerCamelCase name}} = {{asLowerCamelCase name}}; +{{/each}} +{{/if}} + } + } +{{/each}} + + } + \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-class.java.hbs b/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-class.java.hbs new file mode 100644 index 00000000000..5f9de8f654c --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-class.java.hbs @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; +import java.util.Map; +import java.util.LinkedHashMap; + +import org.eclipse.jdt.annotation.NonNull; + +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * {{asUpperCamelCase name}} + * + * @author Dan Cunningham - Initial contribution + */ +public {{#if id}}{{else}}abstract {{/if}}class {{asUpperCamelCase name}}Cluster extends {{#if type}}{{asUpperCamelCase type}}Cluster{{else}}BaseCluster{{/if}} { + + {{#if id}}public static final int CLUSTER_ID = {{asHex id 4}};{{else}}{{/if}} + public static final String CLUSTER_NAME = "{{asUpperCamelCase name}}"; + public static final String CLUSTER_PREFIX = "{{asLowerCamelCase name}}"; +{{#each attributes }} +{{#if (isDepreciated this)}} +{{else}} + public static final String ATTRIBUTE_{{asUpperSnakeCase name}} = "{{asLowerCamelCase name}}"; +{{/if}} +{{/each}} + +{{#each attributes }} +{{#if (isDepreciated this)}} +{{else}} +{{#if details}} + /** + * {{details}} + */ +{{/if}} + public {{{asUpperCamelCase mappedType}}} {{asLowerCamelCase name}}; // {{id}} {{type}} {{access}} +{{/if}} +{{/each}} +{{#each structs}} +{{#if (isFirstElement @index)}} + //Structs +{{/if}} +{{#if details}} + /** + * {{details}} + */ +{{/if}} + public class {{asUpperCamelCase name}} { +{{#each children}} +{{#if details}} + /** + * {{details}} + */ +{{/if}} + public {{{mappedType}}} {{asLowerCamelCase name}}; // {{type}} +{{/each}} + public {{asUpperCamelCase name}}({{#each children}}{{{mappedType}}} {{asLowerCamelCase name}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}) { +{{#each children}} + this.{{asLowerCamelCase name}} = {{asLowerCamelCase name}}; +{{/each}} + } + } +{{/each}} + + +{{#each enums}} +{{#if (isFirstElement @index)}} + //Enums +{{/if}} +{{#if details}} + /** + * {{details}} + */ +{{/if}} + public enum {{asUpperCamelCase name}} implements MatterEnum { +{{#if (isEmpty children)}} + DEFAULT(0, "Default"); +{{else}} +{{#each children}} + {{asEnumField name}}({{id}}, "{{asSpacedTitleCase name}}"){{#unless (isLastElement @index ../children.length)}},{{else}};{{/unless}} +{{/each}} +{{/if}} + public final Integer value; + public final String label; + private {{asUpperCamelCase name}}(Integer value, String label){ + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } +{{/each}} + +{{#each maps}} +{{#if (isFirstElement @index)}} + // Bitmaps +{{/if}} +{{#if details}} + /** + * {{details}} + */ +{{/if}} + public static class {{asUpperCamelCase name}} { +{{#each children}} +{{#if details}} + /** + * {{description}} + * {{details}} + */ +{{/if}} + public {{toBitmapType constraint}} {{toBitmapChildName this ../type}}; +{{/each}} + public {{asUpperCamelCase name}}({{#each children}}{{toBitmapType constraint}} {{toBitmapChildName this ../type}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}){ +{{#each children}} + this.{{toBitmapChildName this ../type}} = {{toBitmapChildName this ../type}}; +{{/each}} + } + } +{{/each}} +{{#if id}} + public {{asUpperCamelCase name}}Cluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, {{id}}, "{{asUpperCamelCase name}}"); + } +{{/if}} + protected {{asUpperCamelCase name}}Cluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } +{{#each commands }} +{{#if (isFirstElement @index)}} + //commands +{{/if}} +{{#if details}} + /** + * {{details}} + */ +{{/if}} + public static ClusterCommand {{asLowerCamelCase name}}({{#each children}}{{#unless (isDepreciated this)}}{{{mappedType}}} {{asLowerCamelCase name}}{{/unless}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}) { +{{#each children}} +{{#if (isFirstElement @index)}} + Map map = new LinkedHashMap<>(); +{{/if}} +{{#unless (isDepreciated this)}} + if ({{asLowerCamelCase name}} != null) { + map.put("{{asLowerCamelCase name}}", {{asLowerCamelCase name}}); + } +{{/unless}} +{{/each}} + return new ClusterCommand("{{asLowerCamelCase name}}"{{#if children}}, map{{/if}}); + } +{{/each}} + @Override + public @NonNull String toString() { + String str = ""; +{{#each attributes }} +{{#if (isDepreciated this)}} +{{else}} + str += "{{asLowerCamelCase name}} : " + {{asLowerCamelCase name}} + "\n"; +{{/if}} +{{/each}} + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-constants.java.hbs b/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-constants.java.hbs new file mode 100644 index 00000000000..296be7754cf --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-constants.java.hbs @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * + * ClusterThingTypes + * @author Dan Cunningham - Initial contribution + */ +public class ClusterConstants { + +{{#each clusters}} + // {{name}} Cluster +{{#each children}} +{{#if (isDepreciated this)}} +{{else}} +{{#if (isAttribute this)}} +{{#if access}} + public static final String CHANNEL_NAME_{{asUpperCase ../name}}_{{asUpperCase name}} = "{{asUpperCamelCase name}}"; + public static final String CHANNEL_LABEL_{{asUpperCase ../name}}_{{asUpperCase name}} = "{{asTitleCase name}}"; + public static final String CHANNEL_ID_{{asUpperCase ../name}}_{{asUpperCase name}} = "{{asLowerCase ../name}}-{{asLowerCase name}}"; + public static final ChannelTypeUID CHANNEL_{{asUpperCase ../name}}_{{asUpperCase name}} = new ChannelTypeUID( + "matter:" + CHANNEL_ID_{{asUpperCase ../name}}_{{asUpperCase name}}); + +{{/if}} +{{/if}} +{{/if}} +{{/each}} +{{/each}} +} diff --git a/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-registry.java.hbs b/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-registry.java.hbs new file mode 100644 index 00000000000..893360f9f53 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/templates/cluster-registry.java.hbs @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * ClusterRegistry + * @author Dan Cunningham - Initial contribution + */ +public class ClusterRegistry { + +public static final Map> CLUSTER_IDS = new HashMap<>(); + static { +{{#each clusters}} +{{#if (isEmpty id)}} +{{else}} + CLUSTER_IDS.put({{id}}, {{asUpperCamelCase name}}Cluster.class); +{{/if}} +{{/each}} + } +} diff --git a/bundles/org.openhab.binding.matter/code-gen/src/templates/data-types-class.java.hbs b/bundles/org.openhab.binding.matter/code-gen/src/templates/data-types-class.java.hbs new file mode 100644 index 00000000000..699447b3886 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/templates/data-types-class.java.hbs @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +/** + * {{asUpperCamelCase name}} + * + * @author Dan Cunningham - Initial contribution + */ + + public class DataTypes { + +{{#each structs}} +{{#if (isFirstElement @index)}} + //Structs +{{/if}} + public class {{asUpperCamelCase name}} { +{{#each children}} + public {{{asUpperCamelCase mappedType}}} {{asLowerCamelCase name}}; // {{type}} +{{/each}} + public {{asUpperCamelCase name}}({{#each children}}{{{asUpperCamelCase mappedType}}} {{asLowerCamelCase name}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}) { +{{#each children}} + this.{{asLowerCamelCase name}} = {{asLowerCamelCase name}}; +{{/each}} + } + } +{{/each}} + + +{{#each enums}} +{{#if (isFirstElement @index)}} + //Enums +{{/if}} + public enum {{asUpperCamelCase name}} implements BaseCluster.MatterEnum { +{{#if (isEmpty children)}} + DEFAULT(0, "Default"); +{{else}} +{{#each children}} + {{asEnumField name}}({{id}}, "{{name}}"){{#unless (isLastElement @index ../children.length)}},{{else}};{{/unless}} +{{/each}} +{{/if}} + public final Integer value; + public final String label; + private {{asUpperCamelCase name}}(Integer value, String label){ + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + +{{/each}} + +{{#each maps}} +{{#if (isFirstElement @index)}} + // Bitmaps +{{/if}} + public static class {{asUpperCamelCase name}} { +{{#if (isEmpty children)}} + public List map; + public {{asUpperCamelCase name}}(List map){ + this.map = map; +{{else}} +{{#each children}} + public boolean {{asLowerCamelCase name}}; +{{/each}} + public {{asUpperCamelCase name}}({{#each children}}boolean {{asLowerCamelCase name}}{{#unless (isLastElement @index ../children.length)}}, {{/unless}}{{/each}}){ +{{#each children}} + this.{{asLowerCamelCase name}} = {{asLowerCamelCase name}}; +{{/each}} +{{/if}} + } + } +{{/each}} + + } + \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/code-gen/src/templates/device-types-class.java.hbs b/bundles/org.openhab.binding.matter/code-gen/src/templates/device-types-class.java.hbs new file mode 100644 index 00000000000..8432d386d86 --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/src/templates/device-types-class.java.hbs @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.util.HashMap; +import java.util.Map; + +/** + * DeviceTypes + * + * @author Dan Cunningham - Initial contribution + */ + + public class DeviceTypes { + + public static final Map DEVICE_MAPPING = new HashMap<>(); + static { +{{#each deviceTypes}} +{{#if (isEmpty id)}} +{{else}} + DEVICE_MAPPING.put({{id}}, "{{asUpperCamelCase name}}"); +{{/if}} +{{/each}} + } +{{#each deviceTypes}} + /** + * {{details}} + **/ + public static final Integer {{asUpperSnakeCase name}} = {{id}}; +{{/each}} + } + \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/code-gen/tsconfig.json b/bundles/org.openhab.binding.matter/code-gen/tsconfig.json new file mode 100644 index 00000000000..c4e36da05ef --- /dev/null +++ b/bundles/org.openhab.binding.matter/code-gen/tsconfig.json @@ -0,0 +1,47 @@ +{ + "compilerOptions": { + // Participate in workspace + "composite": true, + + // Add compatibility with CommonJS modules + "esModuleInterop": true, + + // Compile incrementally using tsbuildinfo state file + "incremental": true, + + // We should probably boost this at least to ES 2017 + "target": "es2020", + + // Generate modules as ES2020 or CommonJS + "module": "commonjs", + + // Use node-style dependency resolution + "moduleResolution": "node", + + "lib": ["ES2015", "DOM"], + + // Do not load globals from node_modules by default + "types": [ + "node" + ], + + // Enforce a subset of our code conventions + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noUnusedParameters": false, + "noUnusedLocals": false, + "strict": true, + "strictNullChecks": true, + "allowJs": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "resolveJsonModule": true + }, + "include": ["src/**/*.ts"], + "ts-node": { + "transpileOnly": true, + "files": true + } +} diff --git a/bundles/org.openhab.binding.matter/doc/pairing.png b/bundles/org.openhab.binding.matter/doc/pairing.png new file mode 100644 index 00000000000..c2bf1507d61 Binary files /dev/null and b/bundles/org.openhab.binding.matter/doc/pairing.png differ diff --git a/bundles/org.openhab.binding.matter/doc/thing-discovery.png b/bundles/org.openhab.binding.matter/doc/thing-discovery.png new file mode 100644 index 00000000000..a0ff6152283 Binary files /dev/null and b/bundles/org.openhab.binding.matter/doc/thing-discovery.png differ diff --git a/bundles/org.openhab.binding.matter/matter-server/package-lock.json b/bundles/org.openhab.binding.matter/matter-server/package-lock.json new file mode 100644 index 00000000000..0ec7ff8de7d --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/package-lock.json @@ -0,0 +1,2216 @@ +{ + "name": "matter-server", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "matter-server", + "version": "0.1.0", + "dependencies": { + "@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "@project-chip/matter.js": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "uuid": "^9.0.1", + "ws": "^8.18.0", + "yargs": "^17.7.2" + }, + "bin": { + "matter-server": "dist/src/app.js" + }, + "devDependencies": { + "@types/bn.js": "^5.1.5", + "@types/node": "^20.9.0", + "@types/uuid": "^9.0.7", + "@types/ws": "^8.5.10", + "@types/yargs": "^17.0.32", + "ts-loader": "^9.4.4", + "ts-node": "^10.9.1", + "typescript": "^5.2.2", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@matter/general": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/general/-/general-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-0rkdLhn/ETSW5w/MQqJQnYRPtOwFx2VKRHAiu5w1lHS7TIVtJ4emPLq2HMSiQ2HMAEDz9eYzfIO4OueEhCbW4A==", + "dependencies": { + "@noble/curves": "^1.9.1" + } + }, + "node_modules/@matter/main": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/main/-/main-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-tu/XAD3wK0kzI9PfXHUK5T0z4Y2vNUMZhbxNe7kSB4kHHjWCHitv1NMG0/uEIWRkSrm4/e82WCdJUrWxLgtm0g==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + }, + "optionalDependencies": { + "@matter/nodejs": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/model": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/model/-/model-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-NvXbecY1WUjXVuXSzZX78uP50rixPNKM8mKP6JRzUpO8sKys1JwZIiJl3kGKPdTuvwot2hwpFJURtnReDam/MA==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/node": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/node/-/node-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-dJt1HoFeomJKfBwcEkK2iAETLJfUVebYzCZ9Ie2qJ37uNc7DfhRomy/Dvj+deHS2Icfalm/COrkMiJQf2G3SvA==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/nodejs": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/nodejs/-/nodejs-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-TTI7HfubBUQEZjcQAmcikoEerTfEt5iCy7ctSwzvt+M0PMToLyj3UrOgDEezezYw+hPzTBmitLWyb/VBnSpk0g==", + "optional": true, + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@matter/protocol": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/protocol/-/protocol-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-y3PNdjtuiA1mAHnfpA3U30y29zQsxW3BOB3anYmGnpKTLTQia6hpmtp3FGHMBMi8rMNW5+PgQT9Gx2f/G+WwFg==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@matter/types": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@matter/types/-/types-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-72AgxSd1PQ5jxILMqt3JJ0Eg+duBekM/Bbjy7RIsXAB6eKPwXvvGypC4jjHn/FrAFbS7s0UVIvezT+xvxQM1gg==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@project-chip/matter.js": { + "version": "0.14.0-alpha.0-20250531-7ed2d6da8", + "resolved": "https://registry.npmjs.org/@project-chip/matter.js/-/matter.js-0.14.0-alpha.0-20250531-7ed2d6da8.tgz", + "integrity": "sha512-CewVH1Ug1eSZBetCICpDs0HJbhi8uAKLMgdiMmkFrf2FwcQb9whdCwQ1FmB0cHThZCNsHLicStqv1S5poS9QkQ==", + "dependencies": { + "@matter/general": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/model": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/protocol": "0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/types": "0.14.0-alpha.0-20250531-7ed2d6da8" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/bn.js": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", + "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.17.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz", + "integrity": "sha512-7zf4YyHA+jvBNfVrk2Gtvs6x7E8V+YDW05bNfG2XkWDJfYRXrTiP/DsB2zSYTaHX0bGIujTBQdMVAhb+j7mwpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", + "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", + "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", + "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.15.0" + }, + "peerDependencies": { + "webpack": "5.x.x", + "webpack-cli": "5.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001710", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001710.tgz", + "integrity": "sha512-B5C0I0UmaGqHgo5FuqJ7hBd4L57A4dDD+Xi+XX1nXOoxGeDdY4Ko38qJYOyqznBVJEqON5p8P1x5zRR3+rsnxA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.132", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.132.tgz", + "integrity": "sha512-QgX9EBvWGmvSRa74zqfnG7+Eno0Ak0vftBll0Pt2/z5b3bEGYL6OUXLgKPtvx73dn3dvwrlyVkjPKRRlhLYTEg==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/envinfo": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", + "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", + "dev": true, + "license": "MIT", + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/interpret": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rechoir": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve": "^1.20.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/schema-utils": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", + "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-loader": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", + "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/watchpack": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.98.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", + "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.1", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", + "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^2.1.1", + "@webpack-cli/info": "^2.0.2", + "@webpack-cli/serve": "^2.0.5", + "colorette": "^2.0.14", + "commander": "^10.0.1", + "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/webpack-merge": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", + "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "flat": "^5.0.2", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/bundles/org.openhab.binding.matter/matter-server/package.json b/bundles/org.openhab.binding.matter/matter-server/package.json new file mode 100644 index 00000000000..35ac1da8643 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/package.json @@ -0,0 +1,43 @@ +{ + "name": "matter-server", + "version": "0.1.0", + "description": "", + "bin": "dist/src/app.js", + "scripts": { + "clean": "tsc --clean", + "build": "tsc --build", + "build-clean": "tsc --build --clean", + "start": "ts-node src/app.ts", + "webpack": "webpack --mode production", + "webpack-dev": "webpack --mode development" + }, + "devDependencies": { + "@types/bn.js": "^5.1.5", + "@types/node": "^20.9.0", + "@types/uuid": "^9.0.7", + "@types/ws": "^8.5.10", + "@types/yargs": "^17.0.32", + "ts-loader": "^9.4.4", + "ts-node": "^10.9.1", + "typescript": "^5.2.2", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" + }, + "dependencies": { + "@matter/main": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "@matter/node": "v0.14.0-alpha.0-20250531-7ed2d6da8", + "@project-chip/matter.js" : "v0.14.0-alpha.0-20250531-7ed2d6da8", + "uuid": "^9.0.1", + "ws": "^8.18.0", + "yargs": "^17.7.2" + }, + "files": [ + "dist/**/*", + "src/**/*", + "LICENSE", + "README.md" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/Controller.ts b/bundles/org.openhab.binding.matter/matter-server/src/Controller.ts new file mode 100644 index 00000000000..848f903a670 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/Controller.ts @@ -0,0 +1,65 @@ +import { WebSocketSession } from "./app"; +import { Request, MessageType } from './MessageTypes'; +import { Logger } from "@matter/general"; +import { printError } from "./util/error"; +const logger = Logger.get("Controller"); + +export abstract class Controller { + + constructor(protected ws: WebSocketSession, protected params: URLSearchParams) { + } + + /** + * Initializes the controller + */ + abstract init(): Promise; + + /** + * Closes the controller + */ + abstract close(): void; + + /** + * Returns the unique identifier of the controller + * @returns + */ + abstract id(): string; + + /** + * Executes a command, similar to a RPC call, on the controller implementor + * @param namespace + * @param functionName + * @param args + */ + abstract executeCommand(namespace: string, functionName: string, args: any[]): any | Promise + + /** + * Handles a request from the client + * @param request + */ + async handleRequest(request: Request): Promise { + const { id, namespace, function: functionName, args } = request; + logger.debug(`Received request: ${Logger.toJSON(request)}`); + try { + const result = this.executeCommand(namespace, functionName, args || []); + if (result instanceof Promise) { + result.then((asyncResult) => { + this.ws.sendResponse(MessageType.ResultSuccess, id, asyncResult); + }).catch((error) => { + printError(logger, error, functionName); + this.ws.sendResponse(MessageType.ResultError, id, undefined, error.message); + }); + } else { + this.ws.sendResponse(MessageType.ResultSuccess, id, result); + } + } catch (error) { + if (error instanceof Error) { + printError(logger,error, functionName); + this.ws.sendResponse(MessageType.ResultError, id, undefined, error.message); + } else { + logger.error(`Unexpected error executing function ${functionName}: ${error}`); + this.ws.sendResponse(MessageType.ResultError, id, undefined, String(error)); + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/MessageTypes.ts b/bundles/org.openhab.binding.matter/matter-server/src/MessageTypes.ts new file mode 100644 index 00000000000..63fcafa0dd0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/MessageTypes.ts @@ -0,0 +1,89 @@ +export interface Request { + id: string; + namespace: string; + function: string; + args?: any[]; +} + +export interface Response { + type: string; + id: string; + result?: any; + error?: string; +} + +export interface Event { + type: string; + data?: any; +} + +export enum EventType { + AttributeChanged = "attributeChanged", + EventTriggered = "eventTriggered", + NodeStateInformation = "nodeStateInformation", + NodeData = "nodeData", + BridgeEvent = "bridgeEvent" +} + +export interface Message { + type: string; + message: any; +} + +export enum MessageType { + Result = "result", + ResultError = "resultError", + ResultSuccess = "resultSuccess", +} + +export enum BridgeEventType { + AttributeChanged = "attributeChanged", + EventTriggered = "eventTriggered", +} +export interface BridgeEvent { + type: string; + data: any; +} + +export interface BridgeAttributeChangedEvent { + endpointId: string; + clusterName: string; + attributeName: string; + data: any; +} + +export interface BridgeEventTrigger { + eventName: string; + data: any; +} + +export enum NodeState { + /** Node is connected, but not fully initialized. */ + CONNECTED = "Connected", + + /** + * Node is disconnected. Data are stale and interactions will most likely return an error. If controller + * instance is still active then the device will be reconnected once it is available again. + */ + DISCONNECTED = "Disconnected", + + /** Node is reconnecting. Data are stale. It is yet unknown if the reconnection is successful. */ + RECONNECTING = "Reconnecting", + + /** + * The node could not be connected and the controller is now waiting for a MDNS announcement and tries every 10 + * minutes to reconnect. + */ + WAITING_FOR_DEVICE_DISCOVERY = "WaitingForDeviceDiscovery", + + /** + * Node structure has changed (Endpoints got added or also removed). Data are up-to-date. + * This State information will only be fired when the subscribeAllAttributesAndEvents option is set to true. + */ + STRUCTURE_CHANGED = "StructureChanged", + + /** + * The node was just Decommissioned. + */ + DECOMMISSIONED = "Decommissioned", +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/app.ts b/bundles/org.openhab.binding.matter/matter-server/src/app.ts new file mode 100644 index 00000000000..44888dbf18e --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/app.ts @@ -0,0 +1,187 @@ +import WebSocket, { Server } from 'ws'; +import { LogFormat, Logger, LogLevel } from "@matter/general"; +import { IncomingMessage } from 'http'; +import { ClientController } from './client/ClientController'; +import { Controller } from './Controller'; +import yargs from 'yargs' +import { hideBin } from 'yargs/helpers' +import { Request, Response, Message, MessageType } from './MessageTypes'; +import { BridgeController } from './bridge/BridgeController'; +import { printError } from './util/error'; +const argv: any = yargs(hideBin(process.argv)).argv + +const logger = Logger.get("matter"); +Logger.level = LogLevel.DEBUG; +Logger.format = LogFormat.PLAIN; + +process.on("SIGINT", () => shutdownHandler("SIGINT")); +process.on("SIGTERM", () => shutdownHandler("SIGTERM")); +process.on('uncaughtException', function (err) { + logger.error(`Caught exception: ${err} ${err.stack}`); +}); + +const parentPid = process.ppid; +setInterval(() => { + try { + // Try sending signal 0 to the parent process. + // If the parent is dead, this will throw an error. + // otherwise we stick around forever and eat 100% of a cpu core (?) + process.kill(parentPid, 0); + } catch (e) { + console.error("Parent process exited. Shutting down Node.js..."); + process.exit(1); + } +}, 5000); + +const shutdownHandler = async (signal: string) => { + logger.info(`Received ${signal}. Closing WebSocket connections...`); + + const closePromises: Promise[] = []; + + wss.clients.forEach((client: WebSocket) => { + if (client.readyState === WebSocket.OPEN) { + closePromises.push( + new Promise((resolve) => { + client.close(1000, "Server shutting down"); + client.on('close', () => { + resolve(); + }); + client.on('error', (err) => { + console.error('Error while closing WebSocket connection:', err); + resolve(); + }); + }) + ); + } + }); + + await Promise.all(closePromises) + .then(() => { + logger.info("All WebSocket connections closed."); + return new Promise((resolve) => wss.close(() => resolve())); + }) + .then(() => { + logger.info("WebSocket server closed."); + process.exit(0); + }) + .catch((err) => { + console.error("Error during shutdown:", err); + process.exit(1); + }); +} + +export interface WebSocketSession extends WebSocket { + controller?: Controller; + sendResponse(type: string, id: string, result?: any, error?: string): void; + sendEvent(type: string, data?: any): void; +} + +const socketPort = argv.port ? parseInt(argv.port) : 8888; +const wss: Server = new WebSocket.Server({ port: socketPort, host: argv.host }); + +wss.on('connection', async (ws: WebSocketSession, req: IncomingMessage) => { + + ws.sendResponse = (type: string, id: string, result?: any, error?: string) => { + const message: Message = { + type: 'response', + message: { + type, + id, + result, + error + } + }; + logger.debug(`Sending response: ${Logger.toJSON(message)}`); + ws.send(Logger.toJSON(message)); + }; + + ws.sendEvent = (type: string, data?: any) => { + const message: Message = { + type: 'event', + message: { + type, + data + } + }; + logger.debug(`Sending event: ${Logger.toJSON(message)}`); + ws.send(Logger.toJSON(message)); + }; + + ws.on('open', () => { + logger.info('WebSocket opened'); + }); + + ws.on('message', (message: string) => { + try { + const request: Request = JSON.parse(message); + ws.controller?.handleRequest(request); + } catch (error) { + if (error instanceof Error) { + ws.sendResponse(MessageType.ResultError, '', undefined, error.message); + } + } + }); + + ws.on('close', async () => { + logger.info('WebSocket closed'); + if (ws.controller) { + await ws.controller.close(); + } + }); + + ws.on('error', (error: Error) => { + logger.error(`WebSocket error: ${error} ${error.stack}`); + }); + + if (!req.url) { + logger.error('No URL in the request'); + ws.close(1002, 'No URL in the request'); + return; + } + + const params = new URLSearchParams(req.url.slice(req.url.indexOf('?'))); + const service = params.get('service') === 'bridge' ? 'bridge' : 'client' + + if (service === 'client') { + let controllerName = params.get('controllerName'); + try { + if (controllerName == null) { + throw new Error('No controllerName parameter in the request'); + } + wss.clients.forEach((client: WebSocket) => { + const session = client as WebSocketSession; + if (session.controller && session.controller.id() === `client-${controllerName}`) { + throw new Error(`Controller with name ${controllerName} already exists!`); + } + }); + ws.controller = new ClientController(ws, params); + await ws.controller.init(); + } catch (error: any) { + printError(logger, error, "ClientController.init()"); + logger.error("returning error", error.message) + ws.close(1002, error.message); + return; + } + } else { + // For now we only support one bridge + const uniqueId = "0" + try { + wss.clients.forEach((client: WebSocket) => { + const session = client as WebSocketSession; + if (session.controller && session.controller.id() === `bridge-${uniqueId}`) { + throw new Error(`Bridge with uniqueId ${uniqueId} already exists!`); + } + }); + ws.controller = new BridgeController(ws, params); + await ws.controller.init(); + } catch (error: any) { + printError(logger, error, "BridgeController.init()"); + logger.error("returning error", error.message) + ws.close(1002, error.message); + return; + } + } + ws.sendEvent('ready', 'Controller initialized'); +}); + +logger.info(`CHIP Controller Server listening on port ${socketPort}`); \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/BridgeController.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/BridgeController.ts new file mode 100644 index 00000000000..511553fb161 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/BridgeController.ts @@ -0,0 +1,56 @@ +import { Logger } from "@matter/general"; +import { Controller } from "../Controller"; +import { WebSocketSession } from "../app"; +import { DeviceNode } from "./DeviceNode"; + +const logger = Logger.get("BridgeController"); + +export class BridgeController extends Controller { + + deviceNode!: DeviceNode + constructor(override ws: WebSocketSession, override params: URLSearchParams) { + super(ws, params); + let storagePath = this.params.get('storagePath'); + + if (storagePath === null) { + throw new Error('No storagePath parameters in the request'); + } + + const deviceName = this.params.get('deviceName'); + const vendorName = this.params.get('vendorName'); + const passcode = this.params.get('passcode'); + const discriminator = this.params.get('discriminator'); + const vendorId = this.params.get('vendorId'); + const productName = this.params.get('productName'); + const productId = this.params.get('productId'); + const port = this.params.get('port'); + + if (deviceName === null || vendorName === null || passcode === null || discriminator === null || vendorId === null || productName === null || productId === null || port === null ) { + throw new Error('Missing parameters in the request'); + } + + this.deviceNode = new DeviceNode(this, storagePath, deviceName, vendorName, parseInt(passcode), parseInt(discriminator), parseInt(vendorId), productName, parseInt(productId), parseInt(port)); + } + override id(): string { + return DeviceNode.DEFAULT_NODE_ID; + } + + override async init() { + } + + executeCommand(namespace: string, functionName: string, args: any[]): any | Promise { + let baseObject: any = this.deviceNode + + logger.debug(`Executing function ${namespace}.${functionName}(${Logger.toJSON(args)})`); + + if (typeof baseObject[functionName] !== 'function') { + throw new Error(`Function ${functionName} not found`); + } + + return baseObject[functionName](...args); + } + + async close() { + return this.deviceNode.close(); + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/DeviceNode.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/DeviceNode.ts new file mode 100644 index 00000000000..11928b93f59 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/DeviceNode.ts @@ -0,0 +1,277 @@ +// Include this first to auto-register Crypto, Network and Time Node.js implementations +import "@matter/node"; + +import { FabricIndex, VendorId } from "@matter/types"; +import { DeviceCommissioner, FabricManager, SessionManager } from "@matter/protocol"; +import { Endpoint, ServerNode } from "@matter/node"; +import { AggregatorEndpoint } from "@matter/node/endpoints"; +import { Environment, Logger, StorageService } from "@matter/general"; +import { GenericDeviceType } from "./devices/GenericDeviceType"; +import { OnOffLightDeviceType } from "./devices/OnOffLightDeviceType"; +import { OnOffPlugInDeviceType } from "./devices/OnOffPlugInDeviceType"; +import { DimmableDeviceType } from "./devices/DimmableDeviceType"; +import { ThermostatDeviceType } from "./devices/ThermostatDeviceType"; +import { WindowCoveringDeviceType } from "./devices/WindowCoveringDeviceType"; +import { BridgeController } from "./BridgeController"; +import { DoorLockDeviceType } from "./devices/DoorLockDeviceType"; +import { TemperatureSensorType } from "./devices/TemperatureSensorType"; +import { HumiditySensorType } from "./devices/HumiditySensorType"; +import { OccupancySensorDeviceType } from "./devices/OccupancySensorDeviceType"; +import { ContactSensorDeviceType } from "./devices/ContactSensorDeviceType"; +import { FanDeviceType } from "./devices/FanDeviceType"; +import { ColorDeviceType } from "./devices/ColorDeviceType"; +import { BridgeEvent, BridgeEventType, EventType } from "../MessageTypes"; +import { BasicInformationServer } from "@matter/node/behaviors"; + +type DeviceType = OnOffLightDeviceType | OnOffPlugInDeviceType | DimmableDeviceType | ThermostatDeviceType | WindowCoveringDeviceType | DoorLockDeviceType | TemperatureSensorType | HumiditySensorType | OccupancySensorDeviceType | ContactSensorDeviceType | FanDeviceType | ColorDeviceType; + +const logger = Logger.get("DeviceNode"); + +/** + * This represents the root device node for the Matter Bridge, creates the initial aggregator endpoint, adds devices to the bridge, and manages storage + */ +export class DeviceNode { + static DEFAULT_NODE_ID = "oh-bridge"; + + private server: ServerNode | null = null; + #environment: Environment = Environment.default; + + private aggregator: Endpoint | null = null; + private devices: Map = new Map(); + private storageService: StorageService; + private inCommissioning: boolean = false; + + constructor(private bridgeController: BridgeController, private storagePath: string, private deviceName: string, private vendorName: string, private passcode: number, private discriminator: number, private vendorId: number, private productName: string, private productId: number, private port: number) { + logger.info(`Device Node Storage location: ${this.storagePath} (Directory)`); + this.#environment.vars.set('storage.path', this.storagePath) + this.storageService = this.#environment.get(StorageService); + } + + //public methods + + async initializeBridge(resetStorage: boolean = false) { + await this.close(); + logger.info(`Initializing bridge`); + await this.#init(); + if (resetStorage) { + logger.info(`!!! Erasing ServerNode Storage !!!`); + await this.server?.erase(); + await this.close(); + // generate a new uniqueId for the bridge (bridgeBasicInformation.uniqueId) + const ohStorage = await this.#ohBridgeStorage(); + await ohStorage.set("basicInformation.uniqueId", BasicInformationServer.createUniqueId()); + logger.info(`Initializing bridge again`); + await this.#init(); + } + logger.info(`Bridge initialized`); + } + + async startBridge() { + if (this.devices.size === 0) { + throw new Error("No devices added, not starting"); + } + if (!this.server) { + throw new Error("Server not initialized, not starting"); + } + if (this.server.lifecycle.isOnline) { + throw new Error("Server is already started, not starting"); + } + this.server.events.commissioning.enabled$Changed.on(async () => { + logger.info(`Commissioning state changed to ${this.server?.state.commissioning.enabled}`); + this.#sendCommissioningStatus(); + }); + this.server.lifecycle.online.on(() => { + logger.info(`Bridge online`); + this.#sendCommissioningStatus(); + }); + logger.info(this.server); + logger.info(`Starting bridge`); + await this.server.start(); + logger.info(`Bridge started`); + const ohStorage = await this.#ohBridgeStorage(); + await ohStorage.set("lastStart", Date.now()); + } + + async close() { + await this.server?.close(); + this.server = null; + this.devices.clear(); + } + + async addEndpoint(deviceType: string, id: string, nodeLabel: string, productName: string, productLabel: string, serialNumber: string, attributeMap: { [key: string]: any }) { + if (this.devices.has(id)) { + throw new Error(`Device ${id} already exists!`); + } + + if (!this.aggregator) { + throw new Error(`Aggregator not initialized, aborting.`); + } + + // little hack to get the correct device class and initialize it with the correct parameters once + const deviceTypeMap: { [key: string]: new (bridgeController: BridgeController, attributeMap: { [key: string]: any }, id: string, nodeLabel: string, productName: string, productLabel: string, serialNumber: string) => DeviceType } = { + "OnOffLight": OnOffLightDeviceType, + "OnOffPlugInUnit": OnOffPlugInDeviceType, + "DimmableLight": DimmableDeviceType, + "Thermostat": ThermostatDeviceType, + "WindowCovering": WindowCoveringDeviceType, + "DoorLock": DoorLockDeviceType, + "TemperatureSensor": TemperatureSensorType, + "HumiditySensor": HumiditySensorType, + "OccupancySensor": OccupancySensorDeviceType, + "ContactSensor": ContactSensorDeviceType, + "Fan": FanDeviceType, + "ColorLight": ColorDeviceType + }; + + const DeviceClass = deviceTypeMap[deviceType]; + if (!DeviceClass) { + throw new Error(`Unsupported device type ${deviceType}`); + } + const device = new DeviceClass(this.bridgeController, attributeMap, id, nodeLabel, productName, productLabel, serialNumber); + this.devices.set(id, device); + await this.aggregator.add(device.endpoint); + } + + async setEndpointState(endpointId: string, clusterName: string, stateName: string, stateValue: any) { + const device = this.devices.get(endpointId); + if (device) { + device.updateState(clusterName, stateName, stateValue); + } + } + + async openCommissioningWindow() { + const dc = this.#getStartedServer().env.get(DeviceCommissioner); + logger.debug('opening basic commissioning window') + await dc.allowBasicCommissioning(() => { + logger.debug('commissioning window closed') + this.inCommissioning = false; + this.#sendCommissioningStatus(); + }); + this.inCommissioning = true; + logger.debug('basic commissioning window open') + this.#sendCommissioningStatus(); + } + + async closeCommissioningWindow() { + const server = this.#getStartedServer(); + if(!server.state.commissioning.commissioned) { + logger.debug('bridge is not commissioned, not closing commissioning window') + this.#sendCommissioningStatus(); + return; + } + const dc = server.env.get(DeviceCommissioner); + logger.debug('closing basic commissioning window') + await dc.endCommissioning(); + } + + getCommissioningState() { + const server = this.#getStartedServer(); + return { + pairingCodes: { + manualPairingCode: server.state.commissioning.pairingCodes.manualPairingCode, + qrPairingCode: server.state.commissioning.pairingCodes.qrPairingCode + }, + commissioningWindowOpen : !server.state.commissioning.commissioned || this.inCommissioning + } + } + + getFabrics() { + const fabricManager = this.#getStartedServer().env.get(FabricManager); + return fabricManager.fabrics; + } + + async removeFabric(fabricIndex: number) { + const fabricManager = this.#getStartedServer().env.get(FabricManager); + await fabricManager.removeFabric(FabricIndex(fabricIndex)); + } + + //private methods + + async #init() { + const ohStorage = await this.#ohBridgeStorage(); + const uniqueId = await this.#uniqueIdForBridge(); + logger.info(`Unique ID: ${uniqueId}`); + /** + * Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration + */ + try { + this.server = await ServerNode.create({ + // Required: Give the Node a unique ID which is used to store the state of this node + id: DeviceNode.DEFAULT_NODE_ID, + + // Provide Network relevant configuration like the port + // Optional when operating only one device on a host, Default port is 5540 + network: { + port: this.port, + }, + + // Provide Commissioning relevant settings + // Optional for development/testing purposes + commissioning: { + passcode: this.passcode, + discriminator: this.discriminator, + + }, + + // Provide Node announcement settings + // Optional: If Ommitted some development defaults are used + productDescription: { + name: this.deviceName, + deviceType: AggregatorEndpoint.deviceType, + }, + + // Provide defaults for the BasicInformation cluster on the Root endpoint + // Optional: If Omitted some development defaults are used + basicInformation: { + vendorName: this.vendorName, + vendorId: VendorId(this.vendorId), + nodeLabel: this.productName, + productName: this.productName, + productLabel: this.productName, + productId: this.productId, + uniqueId: uniqueId, + }, + }); + this.aggregator = new Endpoint(AggregatorEndpoint, { id: "aggregator" }); + await this.server.add(this.aggregator); + await ohStorage.set("basicInformation.uniqueId", uniqueId); + logger.info(`ServerNode created with uniqueId: ${uniqueId}`); + } catch (e) { + logger.error(`Error starting server: ${e}`); + throw e; + } + } + + + #getStartedServer() { + if(!this.server || !this.server.lifecycle.isOnline) { + throw new Error("Server not ready"); + } + return this.server; + } + + async #ohBridgeStorage() { + return (await this.storageService.open(DeviceNode.DEFAULT_NODE_ID)).createContext("openhab"); + } + + async #rootStorage() { + return (await this.storageService.open(DeviceNode.DEFAULT_NODE_ID)).createContext("root"); + } + + async #uniqueIdForBridge() { + const rootContext = await this.#ohBridgeStorage(); + return rootContext.get("basicInformation.uniqueId", BasicInformationServer.createUniqueId()); + } + + #sendCommissioningStatus() { + const state = this.getCommissioningState(); + const be: BridgeEvent = { + type: BridgeEventType.EventTriggered, + data: { + eventName: state.commissioningWindowOpen ? "commissioningWindowOpen" : "commissioningWindowClosed", + data: state.pairingCodes + } + } + this.bridgeController.ws.sendEvent(EventType.BridgeEvent, be); + } +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ColorDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ColorDeviceType.ts new file mode 100644 index 00000000000..03d6468adb8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ColorDeviceType.ts @@ -0,0 +1,90 @@ +import { Endpoint } from "@matter/node"; +import { ExtendedColorLightDevice } from "@matter/node/devices/extended-color-light"; +import { GenericDeviceType } from './GenericDeviceType'; // Adjust the path as needed +import { ColorControlServer } from "@matter/main/behaviors"; +import { ColorControl, LevelControl, OnOff } from "@matter/main/clusters"; + +export class ColorDeviceType extends GenericDeviceType { + + private normalizeValue(value: number, min: number, max: number): number { + return Math.min(Math.max(value, min), max); + } + + override createEndpoint(clusterValues: Record) { + const { colorControl } = clusterValues; + const { colorTempPhysicalMinMireds, colorTempPhysicalMaxMireds } = colorControl; + + colorControl.colorTemperatureMireds = this.normalizeValue( + colorControl.colorTemperatureMireds, + colorTempPhysicalMinMireds, + colorTempPhysicalMaxMireds + ); + + colorControl.startUpColorTemperatureMireds = this.normalizeValue( + colorControl.startUpColorTemperatureMireds, + colorTempPhysicalMinMireds, + colorTempPhysicalMaxMireds + ); + + colorControl.coupleColorTempToLevelMinMireds = this.normalizeValue( + colorControl.coupleColorTempToLevelMinMireds, + colorTempPhysicalMinMireds, + colorTempPhysicalMaxMireds + ); + + colorControl.coupleColorTempToLevelMaxMireds = this.normalizeValue( + colorControl.coupleColorTempToLevelMaxMireds, + colorTempPhysicalMinMireds, + colorTempPhysicalMaxMireds + ); + + const endpoint = new Endpoint(ExtendedColorLightDevice.with( + //setLocally=true for createOnOffServer otherwise moveToHueAndSaturationLogic will not be called b/c matter.js thinks the device is OFF. + this.createOnOffServer(true).with(OnOff.Feature.Lighting), + this.createLevelControlServer().with(LevelControl.Feature.Lighting), + this.createColorControlServer().with(ColorControl.Feature.HueSaturation, ColorControl.Feature.ColorTemperature), + ...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + }); + + return endpoint; + } + + override defaultClusterValues() { + return { + levelControl: { + currentLevel: 0 + }, + onOff: { + onOff: false + }, + colorControl: { + colorMode: 0, + currentHue: 0, + currentSaturation: 0, + colorTemperatureMireds: 154, + startUpColorTemperatureMireds: 154, + colorTempPhysicalMinMireds: 154, + colorTempPhysicalMaxMireds: 667, + coupleColorTempToLevelMinMireds: 154, + coupleColorTempToLevelMaxMireds: 667 + } + } + } + + protected createColorControlServer(): typeof ColorControlServer { + const parent = this; + return class extends ColorControlServer { + override async moveToColorTemperatureLogic(targetMireds: number, transitionTime: number) { + await parent.sendBridgeEvent("colorControl", "colorTemperatureMireds", targetMireds); + return super.moveToColorTemperatureLogic(targetMireds, transitionTime); + } + + override async moveToHueAndSaturationLogic(targetHue: number, targetSaturation: number, transitionTime: number) { + await parent.sendBridgeEvent("colorControl", "currentHue", targetHue); + await parent.sendBridgeEvent("colorControl", "currentSaturation", targetSaturation); + } + }; + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ContactSensorDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ContactSensorDeviceType.ts new file mode 100644 index 00000000000..1b5b744409c --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ContactSensorDeviceType.ts @@ -0,0 +1,28 @@ +import { Endpoint } from "@matter/node"; +import { ContactSensorDevice } from "@matter/node/devices/contact-sensor"; +import { GenericDeviceType } from './GenericDeviceType'; // Adjust the path as needed + +export class ContactSensorDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const defaults = { + booleanState: { + stateValue : false + } + } + const endpoint = new Endpoint(ContactSensorDevice.with(...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + }); + + return endpoint + } + + override defaultClusterValues() { + return { + booleanState: { + stateValue: false + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/DimmableDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/DimmableDeviceType.ts new file mode 100644 index 00000000000..3e071b32c01 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/DimmableDeviceType.ts @@ -0,0 +1,35 @@ +import { Endpoint } from "@matter/node"; +import { DimmableLightDevice } from "@matter/node/devices/dimmable-light"; +import { GenericDeviceType } from './GenericDeviceType'; +import { LevelControl, OnOff } from "@matter/main/clusters"; +import { FixedLabelServer, LevelControlServer, OnOffServer } from "@matter/main/behaviors"; +import { TypeFromPartialBitSchema } from "@matter/main/types"; + +const LevelControlType = LevelControlServer.with(LevelControl.Feature.Lighting); + +export class DimmableDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint(DimmableLightDevice.with( + this.createOnOffServer(), + this.createLevelControlServer().with(LevelControl.Feature.Lighting), + ...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + }); + return endpoint; + } + + override defaultClusterValues() { + return { + levelControl: { + currentLevel: 254 + }, + onOff: { + onOff: false + }, + } + } +} + + diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/DoorLockDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/DoorLockDeviceType.ts new file mode 100644 index 00000000000..52c41f3ed75 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/DoorLockDeviceType.ts @@ -0,0 +1,46 @@ +import { Endpoint } from "@matter/node"; +import { DoorLockDevice } from "@matter/node/devices/door-lock"; +import { GenericDeviceType } from './GenericDeviceType'; +import { DoorLockServer } from "@matter/main/behaviors"; +import { DoorLock } from "@matter/main/clusters"; +import LockState = DoorLock.LockState; + +export class DoorLockDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint(DoorLockDevice.with(...this.defaultClusterServers(), this.createDoorLockServer()), { + ...this.endPointDefaults(), + ...clusterValues + }); + return endpoint + } + + override defaultClusterValues() { + return { + doorLock: { + lockState: 0, + lockType: 2, + actuatorEnabled: true, + doorState: 1, + maxPinCodeLength: 10, + minPinCodeLength: 1, + wrongCodeEntryLimit: 5, + userCodeTemporaryDisableTime: 10, + operatingMode: 0 + } + } + } + + protected createDoorLockServer(): typeof DoorLockServer { + const parent = this; + return class extends DoorLockServer { + override async lockDoor() { + await parent.sendBridgeEvent("doorLock", "lockState", LockState.Locked); + } + + override async unlockDoor() { + await parent.sendBridgeEvent("doorLock", "lockState", LockState.Unlocked); + } + }; + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/FanDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/FanDeviceType.ts new file mode 100644 index 00000000000..a6568d7c3b2 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/FanDeviceType.ts @@ -0,0 +1,72 @@ +import { Endpoint } from "@matter/node"; +import { FanDevice } from "@matter/node/devices/fan"; +import { GenericDeviceType } from "./GenericDeviceType"; +import { FanControl, OnOff } from "@matter/main/clusters"; +import { FanControlServer, OnOffServer } from "@matter/node/behaviors"; +import { Logger } from "@matter/main"; + +const logger = Logger.get("FanDeviceType"); + +export class FanDeviceType extends GenericDeviceType { + override createEndpoint(clusterValues: Record) { + const features: FanControl.Feature[] = []; + if (clusterValues.fanControl.featureMap.step === true) { + features.push(FanControl.Feature.Step); + } + + switch (clusterValues.fanControl.fanModeSequence) { + case FanControl.FanModeSequence.OffLowMedHighAuto: + case FanControl.FanModeSequence.OffLowHighAuto: + case FanControl.FanModeSequence.OffHighAuto: + features.push(FanControl.Feature.Auto); + clusterValues.fanControl.featureMap.auto = true; + break; + default: + clusterValues.fanControl.featureMap.auto = false; + break; + } + + logger.debug(`createEndpoint values: ${JSON.stringify(clusterValues)}`); + const endpoint = new Endpoint( + FanDevice.with( + ...this.defaultClusterServers(), + FanControlServer.with(...features), + ...(clusterValues.onOff?.onOff !== undefined + ? [this.createOnOffServer()] + : []) + ), + { + ...this.endPointDefaults(), + ...clusterValues, + } + ); + endpoint.events.fanControl.fanMode$Changed.on((value) => { + this.sendBridgeEvent("fanControl", "fanMode", value); + }); + + endpoint.events.fanControl.percentSetting$Changed.on((value) => { + this.sendBridgeEvent("fanControl", "percentSetting", value); + }); + + return endpoint; + } + + override defaultClusterValues() { + return { + fanControl: { + featureMap: { + auto: false, + step: false, + multiSpeed: false, + airflowDirection: false, + rocking: false, + wind: false, + }, + fanMode: FanControl.FanMode.Off, + fanModeSequence: FanControl.FanModeSequence.OffHigh, + percentCurrent: 0, + percentSetting: 0, + }, + }; + } +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/GenericDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/GenericDeviceType.ts new file mode 100644 index 00000000000..9437d911ad8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/GenericDeviceType.ts @@ -0,0 +1,157 @@ +import { Endpoint } from "@matter/main"; +import { BridgeController } from "../BridgeController"; +import { EventType, BridgeEvent, BridgeEventType } from '../../MessageTypes'; +import { BridgedDeviceBasicInformationServer } from "@matter/node/behaviors/bridged-device-basic-information"; +import { FixedLabelServer, LevelControlServer, OnOffServer } from "@matter/main/behaviors"; +import { LevelControl, OnOff } from "@matter/main/clusters"; +import { Logger } from "@matter/main"; +import { TypeFromPartialBitSchema } from "@matter/main/types"; + +const logger = Logger.get("GenericDevice"); +const OnOffType = OnOffServer.with(OnOff.Feature.Lighting); + +/** + * This is the base class for all matter device types. + */ +export abstract class GenericDeviceType { + + protected updateLocks = new Set(); + endpoint: Endpoint; + + + constructor(protected bridgeController: BridgeController, protected attributeMap: Record, protected endpointId: string, protected nodeLabel: string, protected productName: string, protected productLabel: string, protected serialNumber: string) { + this.nodeLabel = this.#truncateString(nodeLabel); + this.productLabel = this.#truncateString(productLabel); + this.productName = this.#truncateString(productName); + this.serialNumber = this.#truncateString(serialNumber); + this.endpoint = this.createEndpoint(this.#generateAttributes(this.defaultClusterValues(), attributeMap)); + logger.debug(`New Device: label: ${this.nodeLabel} name: ${this.productName} product label: ${this.productLabel} serial: ${this.serialNumber}`); + } + + abstract defaultClusterValues(): Record; + abstract createEndpoint(clusterValues: Record): Endpoint; + + public async updateState(clusterName: string, attributeName: string, attributeValue: any) { + const args = {} as { [key: string]: any } + args[clusterName] = {} as { [key: string]: any } + args[clusterName][attributeName] = attributeValue + await this.endpoint.set(args); + } + + protected sendBridgeEvent(clusterName: string, attributeName: string, attributeValue: any) { + const be: BridgeEvent = { + type: BridgeEventType.AttributeChanged, + data: { + endpointId: this.endpoint.id, + clusterName: clusterName, + attributeName: attributeName, + data: attributeValue, + } + } + this.sendEvent(EventType.BridgeEvent, be) + } + + protected sendEvent(eventName: string, data: any) { + logger.debug(`Sending event: ${eventName} with data: ${data}`); + this.bridgeController.ws.sendEvent(eventName, data) + } + + protected endPointDefaults() { + return { + id: this.endpointId, + bridgedDeviceBasicInformation: { + nodeLabel: this.nodeLabel, + productName: this.productName, + productLabel: this.productLabel, + serialNumber: this.serialNumber, + reachable: true, + } + } + } + + //note that these overrides assume openHAB will be sending the state back when changed as we will not set it here prematurely + //other wise we would want to call super.on() and so on (same for level control or any other cluster behavior)to set local state + + protected createOnOffServer(setLocally: boolean = false): typeof OnOffType { + const parent = this; + return class extends OnOffType { + override async on() { + await parent.sendBridgeEvent("onOff", "onOff", true); + if(setLocally){ + await super.on(); + } + } + override async off() { + await parent.sendBridgeEvent("onOff", "onOff", false); + if(setLocally){ + await super.off(); + } + } + }; + } + + protected createLevelControlServer(): typeof LevelControlServer { + const parent = this; + return class extends LevelControlServer { + override async moveToLevelLogic( + level: number, + transitionTime: number | null, + withOnOff: boolean, + options: TypeFromPartialBitSchema, + ) { + await parent.sendBridgeEvent("levelControl", "currentLevel", level); + } + }; + } + + protected defaultClusterServers() { + return [ + BridgedDeviceBasicInformationServer, + FixedLabelServer + ] + } + + #truncateString(str: string, maxLength: number = 32): string { + return str.slice(0, maxLength); + } + + #generateAttributes, U extends Partial>(defaults: T, overrides: U): T { + const alwaysAdd = ["fixedLabel"] + let entries = this.#mergeWithDefaults(defaults, overrides); + // Ensure entries include the values from overrides for the keys in alwaysAdd + alwaysAdd.forEach((key) => { + if (key in overrides) { + entries[key as keyof T] = overrides[key as keyof T]!; + } + }); + return entries; + } + + + #mergeWithDefaults, U extends Partial>(defaults: T, overrides: U): T { + function isPlainObject(value: any): value is Record { + return value && typeof value === 'object' && !Array.isArray(value); + } + // Get unique keys from both objects + const allKeys = [...new Set([...Object.keys(defaults), ...Object.keys(overrides)])]; + + return allKeys.reduce((result, key) => { + const defaultValue = defaults[key]; + const overrideValue = overrides[key]; + + // If both values exist and are objects, merge them recursively + if ( + isPlainObject(defaultValue) && + isPlainObject(overrideValue) + ) { + result[key] = this.#mergeWithDefaults(defaultValue, overrideValue); + } else { + // Use override value if it exists, otherwise use default value + result[key] = key in overrides ? overrideValue : defaultValue; + } + + return result; + }, {} as Record) as T; + } + +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/HumiditySensorType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/HumiditySensorType.ts new file mode 100644 index 00000000000..39257e15414 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/HumiditySensorType.ts @@ -0,0 +1,22 @@ +import { Endpoint } from "@matter/node"; +import { HumiditySensorDevice } from "@matter/node/devices/humidity-sensor"; +import { GenericDeviceType } from './GenericDeviceType'; + +export class HumiditySensorType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint(HumiditySensorDevice.with(...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + }); + return endpoint + } + + override defaultClusterValues() { + return { + relativeHumidityMeasurement: { + measuredValue: 0 + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OccupancySensorDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OccupancySensorDeviceType.ts new file mode 100644 index 00000000000..c172cdda9f3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OccupancySensorDeviceType.ts @@ -0,0 +1,34 @@ +import { Endpoint } from "@matter/node"; +import { OccupancySensorDevice } from "@matter/node/devices/occupancy-sensor"; +import { GenericDeviceType } from "./GenericDeviceType"; +import { OccupancySensing } from "@matter/main/clusters"; +import { OccupancySensingServer } from "@matter/node/behaviors"; + +/** + * This is the device type for the occupancy sensor. + */ +export class OccupancySensorDeviceType extends GenericDeviceType { + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint( + OccupancySensorDevice.with( + OccupancySensingServer.with(OccupancySensing.Feature.PassiveInfrared), + ...this.defaultClusterServers()), + { + ...this.endPointDefaults(), + ...clusterValues, + } + ); + return endpoint; + } + + override defaultClusterValues() { + return { + occupancySensing: { + occupancy: { + occupied: false, + }, + occupancySensorType: OccupancySensing.OccupancySensorType.Pir, + }, + }; + } +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OnOffLightDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OnOffLightDeviceType.ts new file mode 100644 index 00000000000..a0c7c8b6ccb --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OnOffLightDeviceType.ts @@ -0,0 +1,24 @@ +import { Endpoint } from "@matter/node"; +import { OnOffLightDevice } from "@matter/node/devices/on-off-light"; +import { GenericDeviceType } from './GenericDeviceType'; +import { OnOff } from "@matter/main/clusters"; + +export class OnOffLightDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint(OnOffLightDevice.with( + ...this.defaultClusterServers(),this.createOnOffServer()), { + ...this.endPointDefaults(), + ...clusterValues + }); + return endpoint + } + + override defaultClusterValues() { + return { + onOff: { + onOff: false + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OnOffPlugInDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OnOffPlugInDeviceType.ts new file mode 100644 index 00000000000..87cf03d755b --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/OnOffPlugInDeviceType.ts @@ -0,0 +1,25 @@ +import { Endpoint } from "@matter/node"; +import { OnOffPlugInUnitDevice } from "@matter/node/devices/on-off-plug-in-unit"; +import { GenericDeviceType } from './GenericDeviceType'; +import { OnOff } from "@matter/main/clusters"; + +export class OnOffPlugInDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint(OnOffPlugInUnitDevice.with( + ...this.defaultClusterServers(), + this.createOnOffServer()), { + ...this.endPointDefaults(), + ...clusterValues + }); + return endpoint + } + + override defaultClusterValues() { + return { + onOff: { + onOff: false + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/TemperatureSensorType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/TemperatureSensorType.ts new file mode 100644 index 00000000000..4be243cf00f --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/TemperatureSensorType.ts @@ -0,0 +1,21 @@ +import { Endpoint } from "@matter/node"; +import { TemperatureSensorDevice } from "@matter/node/devices/temperature-sensor"; +import { GenericDeviceType } from './GenericDeviceType'; + +export class TemperatureSensorType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const endpoint = new Endpoint(TemperatureSensorDevice.with(...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + }); + return endpoint + } + override defaultClusterValues() { + return { + temperatureMeasurement: { + measuredValue: 0 + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ThermostatDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ThermostatDeviceType.ts new file mode 100644 index 00000000000..3c9f414b20a --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/ThermostatDeviceType.ts @@ -0,0 +1,70 @@ +import { Endpoint } from "@matter/node"; +import { ThermostatDevice } from "@matter/node/devices/thermostat"; +import { ThermostatServer } from '@matter/node/behaviors/thermostat'; +import { Thermostat } from '@matter/main/clusters'; +import { GenericDeviceType } from './GenericDeviceType'; + +export class ThermostatDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + let controlSequenceOfOperation = -1; + const features: Thermostat.Feature[] = []; + if (clusterValues.thermostat?.occupiedHeatingSetpoint != undefined) { + features.push(Thermostat.Feature.Heating); + controlSequenceOfOperation = 2; + } + if (clusterValues.thermostat?.occupiedCoolingSetpoint != undefined) { + features.push(Thermostat.Feature.Cooling); + controlSequenceOfOperation = 0; + } + if (features.indexOf(Thermostat.Feature.Heating) != -1 && features.indexOf(Thermostat.Feature.Cooling) != -1) { + features.push(Thermostat.Feature.AutoMode); + controlSequenceOfOperation = 4; + } + + if (controlSequenceOfOperation < 0) { + throw new Error("At least heating, cooling or both must be added") + } + + clusterValues.thermostat.controlSequenceOfOperation = controlSequenceOfOperation; + + const endpoint = new Endpoint(ThermostatDevice.with(this.createOnOffServer().with(), ThermostatServer.with( + ...features + ), ...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + + }); + endpoint.events.thermostat.occupiedHeatingSetpoint$Changed?.on((value) => { + this.sendBridgeEvent('thermostat','occupiedHeatingSetpoint', value); + }); + endpoint.events.thermostat.occupiedCoolingSetpoint$Changed?.on((value) => { + this.sendBridgeEvent('thermostat','occupiedCoolingSetpoint', value); + }); + endpoint.events.thermostat.systemMode$Changed.on((value) => { + this.sendBridgeEvent('thermostat','systemMode', value); + }); + return endpoint; + } + + override defaultClusterValues() { + return { + thermostat: { + systemMode: 0, + localTemperature: 0, + minHeatSetpointLimit: 0, + maxHeatSetpointLimit: 3500, + absMinHeatSetpointLimit: 0, + absMaxHeatSetpointLimit: 3500, + minCoolSetpointLimit: 0, + absMinCoolSetpointLimit: 0, + maxCoolSetpointLimit: 3500, + absMaxCoolSetpointLimit: 3500, + minSetpointDeadBand: 0 + }, + onOff: { + onOff: false + } + } + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/WindowCoveringDeviceType.ts b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/WindowCoveringDeviceType.ts new file mode 100644 index 00000000000..77280bf0084 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/bridge/devices/WindowCoveringDeviceType.ts @@ -0,0 +1,60 @@ +import { Endpoint } from "@matter/node"; +import { WindowCoveringDevice } from "@matter/node/devices/window-covering"; +import { MovementDirection, MovementType, WindowCoveringServer } from '@matter/node/behaviors/window-covering'; +import { WindowCovering } from '@matter/main/clusters'; +import { GenericDeviceType } from './GenericDeviceType'; + +export class WindowCoveringDeviceType extends GenericDeviceType { + + override createEndpoint(clusterValues: Record) { + const features: WindowCovering.Feature[] = []; + features.push(WindowCovering.Feature.Lift); + features.push(WindowCovering.Feature.PositionAwareLift); + const endpoint = new Endpoint(WindowCoveringDevice.with(this.createWindowCoveringServer().with( + ...features, + ), ...this.defaultClusterServers()), { + ...this.endPointDefaults(), + ...clusterValues + }); + endpoint.events.windowCovering.operationalStatus$Changed.on(value => { + this.sendBridgeEvent("windowCovering", "operationalStatus", value); + }); + return endpoint + } + + override defaultClusterValues() { + return { + windowCovering: { + currentPositionLiftPercent100ths: 0, + configStatus: { + operational: true, + onlineReserved: false, + liftMovementReversed: false, + liftPositionAware: true, + tiltPositionAware: false, + liftEncoderControlled: true, + tiltEncoderControlled: false + } + } + } + } + + // this allows us to get all commands to move the device, not just if it thinks the position has changed + private createWindowCoveringServer(): typeof WindowCoveringServer { + const parent = this; + return class extends WindowCoveringServer { + override async handleMovement(type: MovementType, reversed: boolean, direction: MovementDirection, targetPercent100ths?: number): Promise { + if (targetPercent100ths != null) { + await parent.sendBridgeEvent("windowCovering", "targetPositionLiftPercent100ths", targetPercent100ths); + } + } + override async handleStopMovement() { + await parent.sendBridgeEvent("windowCovering", "operationalStatus", { + global: WindowCovering.MovementStatus.Stopped, + lift: WindowCovering.MovementStatus.Stopped, + tilt: WindowCovering.MovementStatus.Stopped + }); + } + }; + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/client/ClientController.ts b/bundles/org.openhab.binding.matter/matter-server/src/client/ClientController.ts new file mode 100644 index 00000000000..3310ba31d83 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/client/ClientController.ts @@ -0,0 +1,70 @@ +import { Logger } from "@matter/general"; +import { ControllerNode } from "./ControllerNode"; +import { Nodes } from "./namespaces/Nodes"; +import { Clusters } from "./namespaces/Clusters"; +import { WebSocketSession } from "../app"; +import { Controller } from "../Controller"; + +const logger = Logger.get("ClientController"); + +/** + * This class exists to expose the "nodes" and "clusters" namespaces to websocket clients + */ +export class ClientController extends Controller { + + nodes?: Nodes; + clusters?: Clusters; + controllerNode: ControllerNode; + controllerName: string; + + constructor(override ws: WebSocketSession, override params: URLSearchParams) { + super(ws, params); + const stringId = this.params.get('nodeId'); + const nodeId = stringId != null ? parseInt(stringId) : null; + let storagePath = this.params.get('storagePath'); + let controllerName = this.params.get('controllerName'); + + if (nodeId === null || storagePath === null || controllerName === null) { + throw new Error('Missing required parameters in the request'); + } + + this.controllerName = controllerName; + this.controllerNode = new ControllerNode(storagePath, controllerName, nodeId, ws); + } + + id(): string { + return "client-" + this.controllerName; + } + + async init() { + await this.controllerNode.initialize(); + logger.info(`Started Node`); + // set up listeners to send events back to the client + this.nodes = new Nodes(this.controllerNode); + this.clusters = new Clusters(this.controllerNode); + } + + async close() { + logger.info(`Closing Node`); + await this.controllerNode?.close(); + logger.info(`Node Closed`); + } + + executeCommand(namespace: string, functionName: string, args: any[]): any | Promise { + const controllerAny: any = this; + let baseObject: any; + + logger.debug(`Executing function ${namespace}.${functionName}(${Logger.toJSON(args)})`); + + if (typeof controllerAny[namespace] !== 'object') { + throw new Error(`Namespace ${namespace} not found`); + } + + baseObject = controllerAny[namespace]; + if (typeof baseObject[functionName] !== 'function') { + throw new Error(`Function ${functionName} not found`); + } + + return baseObject[functionName](...args); + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/client/ControllerNode.ts b/bundles/org.openhab.binding.matter/matter-server/src/client/ControllerNode.ts new file mode 100644 index 00000000000..0f08b23af23 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/client/ControllerNode.ts @@ -0,0 +1,367 @@ +// Include this first to auto-register Crypto, Network and Time Node.js implementations +import { CommissioningController } from "@project-chip/matter.js"; +import { NodeId } from "@matter/types"; +import { PairedNode, NodeStates, Endpoint } from "@project-chip/matter.js/device"; +import { Environment, Logger, StorageContext } from "@matter/general"; +import { ControllerStore } from "@matter/node"; +import { WebSocketSession } from "../app"; +import { EventType, NodeState } from '../MessageTypes'; +import { printError } from "../util/error"; +const logger = Logger.get("ControllerNode"); + +/** + * This class represents the Matter Controller / Admin client + */ +export class ControllerNode { + + private environment: Environment = Environment.default; + private storageContext?: StorageContext; + private nodes: Map = new Map(); + commissioningController?: CommissioningController; + + constructor( + private readonly storageLocation: string, + private readonly controllerName: string, + private readonly nodeNum: number, + private readonly ws: WebSocketSession, + private readonly netInterface?: string + ) { } + + get Store() { + if (!this.storageContext) { + throw new Error("Storage uninitialized"); + } + return this.storageContext; + } + + /** + * Closes the controller node + */ + async close() { + await this.commissioningController?.close(); + this.nodes.clear(); + } + + /** + * Initializes the controller node + */ + async initialize() { + const outputDir = this.storageLocation; + const id = `${this.controllerName}-${this.nodeNum.toString()}` + const prefix = "openHAB: "; + const fabricLabel = prefix + this.controllerName.substring(0, 31 - prefix.length); + + logger.info(`Storage location: ${outputDir} (Directory)`); + this.environment.vars.set('storage.path', outputDir) + + // TODO we may need to choose which network interface to use + if (this.netInterface !== undefined) { + this.environment.vars.set("mdns.networkinterface", this.netInterface); + } + this.commissioningController = new CommissioningController({ + environment: { + environment: this.environment, + id, + }, + autoConnect: false, + adminFabricLabel: fabricLabel, + }); + await this.commissioningController.initializeControllerStore(); + + const controllerStore = this.environment.get(ControllerStore); + // TODO: Implement resetStorage + // if (resetStorage) { + // await controllerStore.erase(); + // } + this.storageContext = controllerStore.storage.createContext("Node"); + + if (await this.Store.has("ControllerFabricLabel")) { + await this.commissioningController.updateFabricLabel( + await this.Store.get("ControllerFabricLabel",fabricLabel), + ); + } + + await this.commissioningController.start(); + } + + /** + * Connects to a node, setting up event listeners. If called multiple times for the same node, it will trigger a node reconnect. + * If a connection timeout is provided, the function will return a promise that will resolve when the node is initialized or reject if the node + * becomes disconnected or the timeout is reached. Note that the node will continue to connect in the background and the client will be notified + * when the node is initialized through the NodeStateInformation event. To stop the reconnection, call the disconnectNode method. + * + * @param nodeId The nodeId of the node to connect to + * @param connectionTimeout Optional timeout in milliseconds. If omitted or non-positive, no timeout will be applied + * @returns Promise that resolves when the node is initialized + * @throws Error if connection times out or node becomes disconnected + */ + async initializeNode(nodeId: string | number, connectionTimeout?: number): Promise { + if (this.commissioningController === undefined) { + throw new Error("CommissioningController not initialized"); + } + + let node = this.nodes.get(NodeId(BigInt(nodeId))); + if (node !== undefined) { + node.triggerReconnect(); + + return new Promise((resolve, reject) => { + let timeoutId: NodeJS.Timeout | undefined; + + if (connectionTimeout && connectionTimeout > 0) { + timeoutId = setTimeout(() => { + logger.info(`Node ${node?.nodeId} state: ${node?.state}`); + if (node?.state === NodeStates.Disconnected || + node?.state === NodeStates.WaitingForDeviceDiscovery || + node?.state === NodeStates.Reconnecting) { + reject(new Error(`Node ${node?.nodeId} reconnection failed: ${NodeStates[node?.state]}`)); + } else { + reject(new Error(`Node ${node?.nodeId} reconnection timed out`)); + } + }, connectionTimeout); + } + + // Cancel timer if node initializes + node?.events.initializedFromRemote.once(() => { + logger.info(`Node ${node?.nodeId} initialized from remote`); + if (timeoutId) clearTimeout(timeoutId); + resolve(); + }); + }); + } + + node = await this.commissioningController.getNode(NodeId(BigInt(nodeId))); + if (node === undefined) { + throw new Error(`Node ${nodeId} not connected`); + } + node.connect(); + this.nodes.set(node.nodeId, node); + + // register event listeners once the node is fully connected + node.events.initializedFromRemote.once(() => { + node.events.attributeChanged.on((data) => { + data.path.nodeId = node.nodeId; + this.ws.sendEvent(EventType.AttributeChanged, data); + }); + + node.events.eventTriggered.on((data) => { + data.path.nodeId = node.nodeId; + this.ws.sendEvent(EventType.EventTriggered, data); + }); + + node.events.stateChanged.on(info => { + const data: any = { + nodeId: node.nodeId, + state: NodeStates[info] + }; + this.ws.sendEvent(EventType.NodeStateInformation, data) + }); + + node.events.structureChanged.on(() => { + const data: any = { + nodeId: node.nodeId, + state: NodeState.STRUCTURE_CHANGED + }; + this.ws.sendEvent(EventType.NodeStateInformation, data) + }); + + node.events.decommissioned.on(() => { + this.nodes.delete(node.nodeId); + const data: any = { + nodeId: node.nodeId, + state: NodeState.DECOMMISSIONED + }; + this.ws.sendEvent(EventType.NodeStateInformation, data) + }); + }); + + return new Promise((resolve, reject) => { + let timeoutId: NodeJS.Timeout | undefined; + + if (connectionTimeout && connectionTimeout > 0) { + timeoutId = setTimeout(() => { + logger.info(`Node ${node?.nodeId} initialization timed out`); + + // register a listener to send the node state information once the node is connected at some future time + node.events.initializedFromRemote.once(() => { + const data: any = { + nodeId: node.nodeId, + state: NodeStates.Connected + }; + this.ws.sendEvent(EventType.NodeStateInformation, data) + }); + + if (node?.state === NodeStates.Disconnected || + node?.state === NodeStates.WaitingForDeviceDiscovery) { + reject(new Error(`Node ${node.nodeId} connection failed: ${NodeStates[node.state]}`)); + } else { + reject(new Error(`Node ${node.nodeId} connection timed out`)); + } + }, connectionTimeout); + } + + node.events.initializedFromRemote.once(() => { + if (timeoutId) clearTimeout(timeoutId); + resolve(); + }); + }); + } + + /** + * Returns a node by nodeId. If the node has not been initialized, it will throw an error. + * @param nodeId + * @returns + */ + getNode(nodeId: number | string | NodeId) { + if (this.commissioningController === undefined) { + throw new Error("CommissioningController not initialized"); + } + //const node = await this.commissioningController.connectNode(NodeId(BigInt(nodeId))) + const node = this.nodes.get(NodeId(BigInt(nodeId))); + if (node === undefined) { + throw new Error(`Node ${nodeId} not connected`); + } + return node; + } + + /** + * Removes a node from the controller + * @param nodeId + */ + async removeNode(nodeId: number | string | NodeId) { + const node = this.nodes.get(NodeId(BigInt(nodeId))); + if (node !== undefined) { + try { + await node.decommission(); + } catch (error) { + logger.error(`Error decommissioning node ${nodeId}: ${error} force removing node`); + await this.commissioningController?.removeNode(NodeId(BigInt(nodeId)), false); + this.nodes.delete(NodeId(BigInt(nodeId))); + } + } else { + await this.commissioningController?.removeNode(NodeId(BigInt(nodeId)), false); + } + } + + /** + * Returns all commissioned nodes Ids + * @returns + */ + async getCommissionedNodes() { + return this.commissioningController?.getCommissionedNodes(); + } + + /** + * Finds the given endpoint, included nested endpoints + * @param node + * @param endpointId + * @returns + */ + getEndpoint(node: PairedNode, endpointId: number) { + const endpoints = node.getDevices(); + for (const e of endpoints) { + const endpoint = this.findEndpoint(e, endpointId); + if (endpoint != undefined) { + return endpoint; + } + } + return undefined; + } + + /** + * + * @param root Endpoints can have child endpoints. This function recursively searches for the endpoint with the given id. + * @param endpointId + * @returns + */ + private findEndpoint(root: Endpoint, endpointId: number): Endpoint | undefined { + if (root.number === endpointId) { + return root; + } + for (const endpoint of root.getChildEndpoints()) { + const found = this.findEndpoint(endpoint, endpointId); + if (found !== undefined) { + return found; + } + } + return undefined; + } + + /** + * Serializes a node and sends it to the web socket + * @param node + * @param endpointId Optional endpointId to serialize. If omitted, all endpoints will be serialized. + */ + sendSerializedNode(node: PairedNode, endpointId?: number) { + this.serializePairedNode(node, endpointId).then(data => { + this.ws.sendEvent(EventType.NodeData, data); + }).catch(error => { + logger.error(`Error serializing node: ${error}`); + printError(logger, error, "serializePairedNode"); + node.triggerReconnect(); + }); + } + + /** + * Serializes a node and returns the json string + * @param node + * @param endpointId Optional endpointId to serialize. If omitted, the root endpoint will be serialized. + * @returns + */ + async serializePairedNode(node: PairedNode, endpointId?: number) { + if (!this.commissioningController) { + throw new Error("CommissioningController not initialized"); + } + + // Recursive function to build the hierarchy + async function serializeEndpoint(endpoint: Endpoint): Promise { + const endpointData: any = { + number: endpoint.number, + clusters: {}, + children: [] + }; + + // Serialize clusters + for (const cluster of endpoint.getAllClusterClients()) { + if (!cluster.id) continue; + + const clusterData: any = { + id: cluster.id, + name: cluster.name + }; + + // Serialize attributes + for (const attributeName in cluster.attributes) { + // Skip numeric referenced attributes + if (/^\d+$/.test(attributeName)) continue; + const attribute = cluster.attributes[attributeName]; + if (!attribute) continue; + const attributeValue = await attribute.get(); + logger.debug(`Attribute ${attributeName} value: ${attributeValue}`); + if (attributeValue !== undefined) { + clusterData[attributeName] = attributeValue; + } + } + + endpointData.clusters[cluster.name] = clusterData; + } + + for (const child of endpoint.getChildEndpoints()) { + endpointData.children.push(await serializeEndpoint(child)); + } + + return endpointData; + } + + // Start serialization from the root endpoint + const rootEndpoint = endpointId !== undefined ? this.getEndpoint(node, endpointId) : node.getRootEndpoint(); + if (rootEndpoint === undefined) { + throw new Error(`Endpoint not found for node ${node.nodeId} and endpointId ${endpointId}`); + } + const data: any = { + id: node.nodeId, + rootEndpoint: await serializeEndpoint(rootEndpoint) + }; + + return data; + } +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/client/namespaces/Clusters.ts b/bundles/org.openhab.binding.matter/matter-server/src/client/namespaces/Clusters.ts new file mode 100644 index 00000000000..93eb07f5f01 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/client/namespaces/Clusters.ts @@ -0,0 +1,212 @@ +import { Logger } from "@matter/general"; +import { ControllerNode } from "../ControllerNode"; +import { convertJsonDataWithModel } from "../../util/Json"; +import { ClusterId, ValidationError } from "@matter/main/types"; +import * as MatterClusters from "@matter/types/clusters"; +import { SupportedAttributeClient } from "@matter/protocol" +import { ClusterModel, MatterModel } from "@matter/model"; +import { camelize, capitalize } from "../../util/String"; + +const logger = Logger.get("Clusters"); + +/** + * This class is used for websocket clients interacting with Matter Clusters to send commands like OnOff, LevelControl, etc... + * Methods not marked as private are intended to be exposed to websocket clients + */ +export class Clusters { + constructor(private controllerNode: ControllerNode) { + } + + /** + * Dynamically executes a command on a specified cluster within a device node. + * This method retrieves the cluster client for the device at the given node and endpoint, checks the existence + * of the command on the cluster, and calls it with any provided arguments. + * + * @param nodeId Identifier for the node containing the target device. + * @param endpointId Endpoint on the node where the command is directed. + * @param clusterName Name of the cluster targeted by the command. + * @param commandName Specific command to be executed on the cluster. + * @param args Optional arguments for executing the command. + * @throws Error if the cluster or command is not found on the device. + */ + async command(nodeId: number, endpointId: number, clusterName: string, commandName: string, args: any) { + logger.debug(`command ${nodeId} ${endpointId} ${clusterName} ${commandName} ${Logger.toJSON(args)}`); + const device = await this.controllerNode.getNode(nodeId).getDeviceById(endpointId); + if (device == undefined) { + throw new Error(`Endpoint ${endpointId} not found`); + } + + const cluster = this.#clusterForName(clusterName); + if (cluster.id === undefined) { + throw new Error(`Cluster ID for ${clusterName} not found`); + } + + const clusterClient = device.getClusterClientById(ClusterId(cluster.id)); + if (clusterClient === undefined) { + throw new Error(`Cluster client for ${clusterName} not found`); + } + + const uppercaseName = capitalize(commandName); + const command = cluster.commands.find(c => c.name === uppercaseName); + if (command == undefined) { + throw new Error(`Cluster Function ${commandName} not found`); + } + + let convertedArgs: any = undefined; + if (args !== undefined && Object.keys(args).length > 0) { + convertedArgs = convertJsonDataWithModel(command, args); + } + + return clusterClient.commands[commandName](convertedArgs); + } + + /** + * Writes an attribute to a device (not all attributes are writable) + * @param nodeId + * @param endpointId + * @param clusterName + * @param attributeName + * @param value + */ + async writeAttribute(nodeId: number, endpointId: number, clusterName: string, attributeName: string, value: string) { + let parsedValue: any; + try { + parsedValue = JSON.parse(value); + } catch (error) { + try { + parsedValue = JSON.parse(`"${value}"`); + } catch (innerError) { + throw new Error(`ERROR: Could not parse value ${value} as JSON.`) + } + } + + const device = await this.controllerNode.getNode(nodeId).getDeviceById(endpointId); + if (device == undefined) { + throw new Error(`Endpoint ${endpointId} not found`); + } + + const cluster = this.#clusterForName(clusterName); + if (cluster.id === undefined) { + throw new Error(`Cluster ID for ${clusterName} not found`); + } + + const clusterClient = device.getClusterClientById(ClusterId(cluster.id)); + if (clusterClient === undefined) { + throw new Error(`Cluster client for ${clusterName} not found`); + } + + const attributeClient = clusterClient.attributes[attributeName]; + if (!(attributeClient instanceof SupportedAttributeClient)) { + throw new Error(`Attribute ${nodeId}/${endpointId}/${clusterName}/${attributeName} not supported.`) + } + + const uppercaseName = capitalize(attributeName); + const attribute = cluster.attributes.find(c => c.name === uppercaseName); + if (attribute == undefined) { + throw new Error(`Attribute ${attributeName} not found`); + } + + try { + parsedValue = convertJsonDataWithModel(attribute, parsedValue); + await attributeClient.set(parsedValue); + console.log( + `Attribute ${attributeName} ${nodeId}/${endpointId}/${clusterName}/${attributeName} set to ${Logger.toJSON(value)}`, + ); + } catch (error) { + if (error instanceof ValidationError) { + throw new Error(`Could not validate data for attribute ${attributeName} to ${Logger.toJSON(parsedValue)}: ${error}${error.fieldName !== undefined ? ` in field ${error.fieldName}` : ""}`, + ) + } else { + throw new Error(`Could not set attribute ${attributeName} to ${Logger.toJSON(parsedValue)}: ${error}`) + } + } + } + + /** + * Reads an attribute from a device + * @param nodeId + * @param endpointId + * @param clusterName + * @param attributeName + */ + async readAttribute(nodeId: number, endpointId: number, clusterName: string, attributeName: string) { + const device = await this.controllerNode.getNode(nodeId).getDeviceById(endpointId); + if (device == undefined) { + throw new Error(`Endpoint ${endpointId} not found`); + } + + const cluster = this.#clusterForName(clusterName); + if (cluster.id === undefined) { + throw new Error(`Cluster ID for ${clusterName} not found`); + } + + const clusterClient = device.getClusterClientById(ClusterId(cluster.id)); + if (clusterClient === undefined) { + throw new Error(`Cluster client for ${clusterName} not found`); + } + + const attributeClient = clusterClient.attributes[attributeName]; + if (!(attributeClient instanceof SupportedAttributeClient)) { + throw new Error(`Attribute ${nodeId}/${endpointId}/${clusterName}/${attributeName} not supported.`) + } + + const uppercaseName = capitalize(attributeName); + const attribute = cluster.attributes.find(c => c.name === uppercaseName); + if (attribute == undefined) { + throw new Error(`Attribute ${attributeName} not found`); + } + + return await attributeClient.get(true); + } + + /** + * Requests all attributes data for a single endpoint and its children + * @param nodeId + * @param endpointId + * @returns + */ + async readCluster(nodeId: string | number, endpointId: number, clusterNameOrId: string | number) { + const device = await this.controllerNode.getNode(nodeId).getDeviceById(endpointId); + if (device === undefined) { + throw new Error(`Endpoint ${endpointId} not found`); + } + + const clusterId = typeof clusterNameOrId === 'string' ? this.#clusterForName(clusterNameOrId).id : clusterNameOrId; + if (clusterId === undefined) { + throw new Error(`Cluster ID for ${clusterNameOrId} not found`); + } + + const clusterClient = device.getClusterClientById(ClusterId(clusterId)); + if (clusterClient === undefined) { + throw new Error(`Cluster client for ${clusterNameOrId} not found`); + } + + const clusterData: any = { + id: clusterClient.id, + name: clusterClient.name + }; + + // Serialize attributes + for (const attributeName in clusterClient.attributes) { + // Skip numeric referenced attributes + if (/^\d+$/.test(attributeName)) continue; + const attribute = clusterClient.attributes[attributeName]; + if (!attribute) continue; + const attributeValue = await attribute.get(); + logger.debug(`Attribute ${attributeName} value: ${attributeValue}`); + if (attributeValue !== undefined) { + clusterData[attributeName] = attributeValue; + } + } + + return clusterData; + } + + #clusterForName(clusterName: string): ClusterModel { + const cluster = MatterModel.standard.clusters.find(c => c.name === clusterName); + if (cluster == null) { + throw new Error(`Cluster ${clusterName} not found`); + } + return cluster; + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/client/namespaces/Nodes.ts b/bundles/org.openhab.binding.matter/matter-server/src/client/namespaces/Nodes.ts new file mode 100644 index 00000000000..82c55946ea8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/client/namespaces/Nodes.ts @@ -0,0 +1,281 @@ +import { NodeCommissioningOptions } from "@project-chip/matter.js"; +import { GeneralCommissioning, OperationalCredentialsCluster } from "@matter/main/clusters"; +import { ManualPairingCodeCodec, QrPairingCodeCodec, QrCode, NodeId, FabricIndex } from "@matter/types"; +import { Logger } from "@matter/main"; +import { ControllerNode } from "../ControllerNode"; + +const logger = Logger.get("matter"); + + +/** + * This class is used for exposing Matter nodes. This includes node lifecycle functions, node fabrics, node data, and other node related methods to websocket clients. + * Methods not marked as private are intended to be exposed to websocket clients + */ +export class Nodes { + + constructor(private controllerNode: ControllerNode) { + } + + /** + * Returns all commissioned nodes Ids + * @returns + */ + async listNodes() { + if (this.controllerNode.commissioningController === undefined) { + throw new Error("CommissioningController not initialized"); + } + return this.controllerNode.getCommissionedNodes(); + } + + /** + * Initializes a node and connects to it + * @param nodeId + * @returns + */ + async initializeNode(nodeId: string | number) { + return this.controllerNode.initializeNode(nodeId); + } + + /** + * Requests all attributes data for a node + * @param nodeId + * @returns + */ + async requestAllData(nodeId: string | number) { + const node = await this.controllerNode.getNode(nodeId); + if (node.initialized) { + return this.controllerNode.sendSerializedNode(node); + } else { + throw new Error(`Node ${nodeId} not initialized`); + } + } + + /** + * Requests all attributes data for a single endpoint and its children + * @param nodeId + * @param endpointId + * @returns + */ + async requestEndpointData(nodeId: string | number, endpointId: number) { + const node = await this.controllerNode.getNode(nodeId); + if (node.initialized) { + return this.controllerNode.sendSerializedNode(node, endpointId); + } else { + throw new Error(`Node ${nodeId} not initialized`); + } + } + + /** + * Pairs a node using a pairing code, supports multiple pairing code formats + * @param pairingCode + * @param shortDiscriminator + * @param setupPinCode + * @returns + */ + async pairNode(pairingCode: string | undefined, shortDiscriminator: number | undefined, setupPinCode: number | undefined) { + let discriminator: number | undefined; + let nodeIdStr: string | undefined; + let ipPort: number | undefined; + let ip: string | undefined; + let instanceId: string | undefined; + let ble = false + + if (typeof pairingCode === "string" && pairingCode.trim().length > 0) { + pairingCode = pairingCode.trim(); + if (pairingCode.toUpperCase().indexOf('MT:') == 0) { + const qrcode = QrPairingCodeCodec.decode(pairingCode.toUpperCase())[0]; + setupPinCode = qrcode.passcode; + discriminator = qrcode.discriminator; + } else { + const { shortDiscriminator: pairingCodeShortDiscriminator, passcode } = + ManualPairingCodeCodec.decode(pairingCode); + shortDiscriminator = pairingCodeShortDiscriminator; + setupPinCode = passcode; + discriminator = undefined; + } + } else if (discriminator === undefined && shortDiscriminator === undefined) { + discriminator = 3840; + } + + const nodeId = nodeIdStr !== undefined ? NodeId(BigInt(nodeIdStr)) : undefined; + if (this.controllerNode.commissioningController === undefined) { + throw new Error("CommissioningController not initialized"); + } + + const options = { + discovery: { + knownAddress: + ip !== undefined && ipPort !== undefined + ? { ip, port: ipPort, type: "udp" } + : undefined, + identifierData: + instanceId !== undefined + ? { instanceId } + : discriminator !== undefined + ? { longDiscriminator: discriminator } + : shortDiscriminator !== undefined + ? { shortDiscriminator } + : {}, + discoveryCapabilities: { + ble, + onIpNetwork: true, + }, + }, + passcode: setupPinCode + } as NodeCommissioningOptions; + + options.commissioning = { + nodeId: nodeId !== undefined ? NodeId(nodeId) : undefined, + regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.Outdoor, // Set to the most restrictive if relevant + regulatoryCountryCode: "XX" + }; + + if (this.controllerNode.Store.has("WiFiSsid") && this.controllerNode.Store.has("WiFiPassword")) { + options.commissioning.wifiNetwork = { + wifiSsid: await this.controllerNode.Store.get("WiFiSsid", ""), + wifiCredentials: await this.controllerNode.Store.get("WiFiPassword", ""), + }; + } + if ( + this.controllerNode.Store.has("ThreadName") && + this.controllerNode.Store.has("ThreadOperationalDataset") + ) { + options.commissioning.threadNetwork = { + networkName: await this.controllerNode.Store.get("ThreadName", ""), + operationalDataset: await this.controllerNode.Store.get( + "ThreadOperationalDataset", + "", + ), + }; + } + + const commissionedNodeId = + await this.controllerNode.commissioningController.commissionNode(options); + + console.log(`Commissioned Node: ${commissionedNodeId}`); + return commissionedNodeId; + } + + /** + * Disconnects a node + * @param nodeId + */ + async disconnectNode(nodeId: number | string) { + const node = this.controllerNode.getNode(nodeId); + await node.disconnect(); + } + + /** + * Reconnects a node + * @param nodeId + */ + async reconnectNode(nodeId: number | string) { + const node = await this.controllerNode.getNode(nodeId); + node.triggerReconnect(); + } + + /** + * Returns the fabrics for a node. Fabrics are the set of matter networks that the node has been commissioned to (openhab, Alexa, Google, Apple, etc) + * @param nodeId + * @returns + */ + async getFabrics(nodeId: number | string) { + const node = await this.controllerNode.getNode(nodeId); + const operationalCredentialsCluster = node.getRootClusterClient(OperationalCredentialsCluster); + if (operationalCredentialsCluster === undefined) { + throw new Error(`OperationalCredentialsCluster for node ${nodeId} not found.`); + } + return await operationalCredentialsCluster.getFabricsAttribute(true, false); + } + + /** + * Removes a fabric from a node, effectively decommissioning the node from the specific network + * @param nodeId + * @param index + * @returns + */ + async removeFabric(nodeId: number | string, index: number) { + if (this.controllerNode.commissioningController === undefined) { + console.log("Controller not initialized, nothing to disconnect."); + return; + } + + const node = await this.controllerNode.getNode(nodeId); + if (node === undefined) { + throw new Error(`Node ${nodeId} not found`); + } + const operationalCredentialsCluster = node.getRootClusterClient(OperationalCredentialsCluster); + + if (operationalCredentialsCluster === undefined) { + throw new Error(`OperationalCredentialsCluster for node ${nodeId} not found.`); + } + + + const fabricInstance = FabricIndex(index); + const ourFabricIndex = await operationalCredentialsCluster.getCurrentFabricIndexAttribute(true); + + if (ourFabricIndex == fabricInstance) { + throw new Error("Will not delete our own fabric"); + } + + await operationalCredentialsCluster.commands.removeFabric({ fabricIndex: fabricInstance }); + } + + /** + * Removes a node from the commissioning controller + * @param nodeId + */ + async removeNode(nodeId: number | string) { + await this.controllerNode.removeNode(nodeId); + } + + /** + * Returns active session information for all connected nodes. + * @returns + */ + sessionInformation() { + return this.controllerNode.commissioningController?.getActiveSessionInformation() || {} + } + + /** + * Opens a basic commissioning window for a node allowing for manual pairing to an additional fabric. + * @param nodeId + * @param timeout + */ + async basicCommissioningWindow(nodeId: number | string, timeout = 900) { + const node = await this.controllerNode.getNode(nodeId); + await node.openBasicCommissioningWindow(timeout); + console.log(`Basic Commissioning Window for node ${nodeId} opened`); + } + + /** + * Opens an enhanced commissioning window for a node allowing for QR code pairing to an additional fabric. + * @param nodeId + * @param timeout + * @returns + */ + async enhancedCommissioningWindow(nodeId: number | string, timeout = 900) { + const node = await this.controllerNode.getNode(nodeId); + const data = await node.openEnhancedCommissioningWindow(timeout); + + console.log(`Enhanced Commissioning Window for node ${nodeId} opened`); + const { qrPairingCode, manualPairingCode } = data; + + console.log(QrCode.get(qrPairingCode)); + console.log( + `QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`, + ); + console.log(`Manual pairing code: ${manualPairingCode}`); + return data; + } + + /** + * Logs the structure of a node + * @param nodeId + */ + async logNode(nodeId: number | string) { + const node = await this.controllerNode.getNode(nodeId); + console.log("Logging structure of Node ", node.nodeId.toString()); + node.logStructure(); + } +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/util/Json.ts b/bundles/org.openhab.binding.matter/matter-server/src/util/Json.ts new file mode 100644 index 00000000000..f1e7a38f3cd --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/util/Json.ts @@ -0,0 +1,74 @@ +import { Bytes } from "@matter/general"; +import { ValueModel } from "@matter/model"; +import { ValidationDatatypeMismatchError } from "@matter/types"; +import { camelize } from "./String"; +import { Logger } from "@matter/general"; + +const logger = Logger.get("Clusters"); + +export function convertJsonDataWithModel(model: ValueModel, data: any): any { + const definingModel = model.definingModel ?? model; + logger.debug("convertJsonDataWithModel: type {}", definingModel.effectiveMetatype); + logger.debug("convertJsonDataWithModel: data {}", data); + logger.debug("convertJsonDataWithModel: model {}", model); + switch (definingModel.effectiveMetatype) { + case "array": + if (!Array.isArray(data)) { + throw new ValidationDatatypeMismatchError(`Expected array, got ${typeof data}`); + } + return data.map(item => convertJsonDataWithModel(definingModel.children[0], item)); + case "object": + if (typeof data !== "object") { + throw new ValidationDatatypeMismatchError(`Expected object, got ${typeof data}`); + } + for (const child of definingModel.children) { + const childKeyName = camelize(child.name); + data[childKeyName] = convertJsonDataWithModel(child, data[childKeyName]); + } + return data; + case "integer": + if (typeof data === "string") { + if (definingModel.metabase?.byteSize !== undefined && definingModel.metabase.byteSize > 6) { + // If we have an integer with byteSize > 6 and a string value, we need to convert the string to a + // BigInt also handles 0x prefixed hex strings + return BigInt(data); + } else if (data.startsWith("0x")) { + // Else if hex string convert to number + return parseInt(data.substring(2), 16); + } + } + break; + case "bytes": + if (typeof data === "string") { + // ByteArray encoded as hex-String ... so convert to ByteArray + return Bytes.fromHex(data); + } + break; + } + + return data; +} + +export function toJSON(data: any) { + return JSON.stringify(data, (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + if (value instanceof Uint8Array) { + return Bytes.toHex(value); + } + if (value === undefined) { + return "undefined"; + } + return value; + }); +} + +export function fromJSON(data: any) { + return JSON.parse(data, (key, value) => { + if (typeof value === "string" && value.startsWith("0x")) { + return Bytes.fromHex(value); + } + return value; + }); +} \ No newline at end of file diff --git a/bundles/org.openhab.binding.matter/matter-server/src/util/String.ts b/bundles/org.openhab.binding.matter/matter-server/src/util/String.ts new file mode 100644 index 00000000000..33c863d1485 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/util/String.ts @@ -0,0 +1,7 @@ +export function camelize(str: string) { + return str.charAt(0).toLowerCase() + str.slice(1); +} + +export function capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); +} diff --git a/bundles/org.openhab.binding.matter/matter-server/src/util/error.ts b/bundles/org.openhab.binding.matter/matter-server/src/util/error.ts new file mode 100644 index 00000000000..11dab83ef57 --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/src/util/error.ts @@ -0,0 +1,20 @@ +import { Logger } from "@matter/general"; + +export function printError(logger: Logger, error: Error, functionName: String) { + + logger.error(`Error executing function ${functionName}: ${error.message}`); + logger.error(`Stack trace: ${error.stack}`); + + // Log additional error properties if available + if ('code' in error) { + logger.error(`Error code: ${(error as any).code}`); + } + if ('name' in error) { + logger.error(`Error name: ${(error as any).name}`); + } + + // Fallback: log the entire error object in case there are other useful details + logger.error(`Full error object: ${JSON.stringify(error, Object.getOwnPropertyNames(error), 2)}`) + logger.error("--------------------------------") + logger.error(error) +} diff --git a/bundles/org.openhab.binding.matter/matter-server/tsconfig.json b/bundles/org.openhab.binding.matter/matter-server/tsconfig.json new file mode 100644 index 00000000000..01869671bdd --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + // Participate in workspace + "composite": true, + + // Add compatibility with CommonJS modules + "esModuleInterop": true, + + // Compile incrementally using tsbuildinfo state file + "incremental": true, + + // We should probably boost this at least to ES 2017 + "target": "es2022", + + // Generate modules as ES2020 or CommonJS + "module": "node16", + + // Use node-style dependency resolution + "moduleResolution": "node16", + + "lib": ["ES2022", "DOM"], + + // Do not load globals from node_modules by default + "types": [ + "node" + ], + + // Enforce a subset of our code conventions + "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, + "noImplicitOverride": true, + "noUnusedParameters": false, + "noUnusedLocals": false, + "strict": true, + "strictNullChecks": true, + "allowJs": true, + "skipLibCheck": true, + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +} diff --git a/bundles/org.openhab.binding.matter/matter-server/webpack.config.js b/bundles/org.openhab.binding.matter/matter-server/webpack.config.js new file mode 100644 index 00000000000..35e513f0efd --- /dev/null +++ b/bundles/org.openhab.binding.matter/matter-server/webpack.config.js @@ -0,0 +1,22 @@ +const path = require('path'); + +module.exports = { + entry: './src/app.ts', + target: 'node', + externals: {bufferutil: "bufferutil", "utf-8-validate": "utf-8-validate"}, + module: { + rules: [ + { + test: /\.tsx?$/, + use: 'ts-loader' + } + ] + }, + resolve: { + extensions: ['.tsx', '.ts', '.js'] + }, + output: { + filename: 'matter.js', // single output JS file + path: path.resolve(__dirname, 'dist') + } +}; diff --git a/bundles/org.openhab.binding.matter/pom.xml b/bundles/org.openhab.binding.matter/pom.xml new file mode 100644 index 00000000000..944b43daef1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/pom.xml @@ -0,0 +1,234 @@ + + + + 4.0.0 + + + org.openhab.addons.bundles + org.openhab.addons.reactor.bundles + 5.0.0-SNAPSHOT + + + org.openhab.binding.matter + + openHAB Add-ons :: Bundles :: Matter Binding + + + src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen + + + + + + com.github.eirslett + frontend-maven-plugin + + v20.12.2 + 10.5.0 + + + + + + Install node and npm + + install-node-and-npm + + generate-resources + + matter-server + + + + + npm install + + npm + + + matter-server + install + + + + + npm run webpack-dev + + npm + + + matter-server + run webpack-dev + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + add-resource + + generate-resources + + + + matter-server/dist + matter.js + matter-server + + + + + + + + + + + + code-gen + + + + + com.github.eirslett + frontend-maven-plugin + + v20.12.2 + 10.5.0 + + + + Install node and npm for codegen + + install-node-and-npm + + generate-sources + + code-gen + + + + + npm-install-codegen + + npm + + generate-sources + + code-gen + install + + + + + npm-run-codegen + + npm + + generate-sources + + code-gen + run start + + + + + + + + maven-clean-plugin + + + clean-generated-code + + clean + + clean + + + + code-gen/out + + + ${generated.code.dir} + + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-generated-code + + copy-resources + + generate-sources + + ${generated.code.dir} + + + code-gen/out + + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + cleanup-output + + run + + generate-sources + + + + + + + + + + + + com.diffplug.spotless + spotless-maven-plugin + + + + codestyle_check + none + + + + format-generated-code + + apply + + process-sources + + + + + + + + diff --git a/bundles/org.openhab.binding.matter/src/main/feature/feature.xml b/bundles/org.openhab.binding.matter/src/main/feature/feature.xml new file mode 100644 index 00000000000..745d84386d8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/feature/feature.xml @@ -0,0 +1,9 @@ + + + mvn:org.openhab.core.features.karaf/org.openhab.core.features.karaf.openhab-core/${ohc.version}/xml/features + + + openhab-runtime-base + mvn:org.openhab.addons.bundles/org.openhab.binding.matter/${project.version} + + diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java new file mode 100644 index 00000000000..eb2f6e89a5e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterBindingConstants.java @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * The {@link MatterBindingConstants} class defines common constants, which are + * used across the whole binding. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterBindingConstants { + public static final String BINDING_ID = "matter"; + // List of all Thing Type UIDs + public static final ThingTypeUID THING_TYPE_CONTROLLER = new ThingTypeUID(BINDING_ID, "controller"); + public static final ThingTypeUID THING_TYPE_NODE = new ThingTypeUID(BINDING_ID, "node"); + public static final ThingTypeUID THING_TYPE_ENDPOINT = new ThingTypeUID(BINDING_ID, "endpoint"); + public static final String CONFIG_DESCRIPTION_URI_THING_PREFIX = "thing"; + // List of Channel UIDs + public static final String CHANNEL_ID_ONOFF_ONOFF = "onoffcontrol-onoff"; + public static final ChannelTypeUID CHANNEL_ONOFF_ONOFF = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_ONOFF_ONOFF); + public static final String CHANNEL_ID_LEVEL_LEVEL = "levelcontrol-level"; + public static final ChannelTypeUID CHANNEL_LEVEL_LEVEL = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_LEVEL_LEVEL); + public static final String CHANNEL_ID_COLOR_COLOR = "colorcontrol-color"; + public static final ChannelTypeUID CHANNEL_COLOR_COLOR = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_COLOR_COLOR); + public static final String CHANNEL_ID_COLOR_TEMPERATURE = "colorcontrol-temperature"; + public static final ChannelTypeUID CHANNEL_COLOR_TEMPERATURE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_COLOR_TEMPERATURE); + public static final String CHANNEL_ID_COLOR_TEMPERATURE_ABS = "colorcontrol-temperature-abs"; + public static final ChannelTypeUID CHANNEL_COLOR_TEMPERATURE_ABS = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_COLOR_TEMPERATURE_ABS); + public static final String CHANNEL_ID_POWER_BATTERYPERCENT = "powersource-batpercentremaining"; + public static final ChannelTypeUID CHANNEL_POWER_BATTERYPERCENT = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_POWER_BATTERYPERCENT); + public static final String CHANNEL_ID_POWER_CHARGELEVEL = "powersource-batchargelevel"; + public static final ChannelTypeUID CHANNEL_POWER_CHARGELEVEL = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_POWER_CHARGELEVEL); + public static final String CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE = "thermostat-localtemperature"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_LOCALTEMPERATURE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE); + public static final String CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE = "thermostat-outdoortemperature"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OUTDOORTEMPERATURE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE); + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLING = "thermostat-occupiedcooling"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDCOOLING = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLING); + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATING = "thermostat-occupiedheating"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDHEATING = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATING); + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLING = "thermostat-unoccupiedcooling"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDCOOLING = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLING); + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATING = "thermostat-unoccupiedheating"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDHEATING = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATING); + public static final String CHANNEL_ID_THERMOSTAT_SYSTEMMODE = "thermostat-systemmode"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SYSTEMMODE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_SYSTEMMODE); + public static final String CHANNEL_ID_THERMOSTAT_RUNNINGMODE = "thermostat-runningmode"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_RUNNINGMODE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_RUNNINGMODE); + public static final String CHANNEL_ID_THERMOSTAT_HEATING_DEMAND = "thermostat-heatingdemand"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_HEATING_DEMAND = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_HEATING_DEMAND); + public static final String CHANNEL_ID_THERMOSTAT_COOLING_DEMAND = "thermostat-coolingdemand"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_COOLING_DEMAND = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_THERMOSTAT_COOLING_DEMAND); + public static final String CHANNEL_ID_DOORLOCK_STATE = "doorlock-lockstate"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_STATE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_DOORLOCK_STATE); + public static final String CHANNEL_ID_WINDOWCOVERING_LIFT = "windowcovering-lift"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_LIFT = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_WINDOWCOVERING_LIFT); + public static final String CHANNEL_ID_FANCONTROL_PERCENT = "fancontrol-percent"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_PERCENT = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_FANCONTROL_PERCENT); + public static final String CHANNEL_ID_FANCONTROL_MODE = "fancontrol-fanmode"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_MODE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_FANCONTROL_MODE); + public static final String CHANNEL_ID_TEMPERATUREMEASURMENT_MEASUREDVALUE = "temperaturemeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_TEMPERATUREMEASURMENT_MEASUREDVALUE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_TEMPERATUREMEASURMENT_MEASUREDVALUE); + public static final String CHANNEL_ID_HUMIDITYMEASURMENT_MEASUREDVALUE = "relativehumiditymeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_HUMIDITYMEASURMENT_MEASUREDVALUE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_HUMIDITYMEASURMENT_MEASUREDVALUE); + public static final String CHANNEL_ID_OCCUPANCYSENSING_OCCUPIED = "occupancysensing-occupied"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_OCCUPIED = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_OCCUPANCYSENSING_OCCUPIED); + public static final String CHANNEL_ID_ILLUMINANCEMEASURMENT_MEASUREDVALUE = "illuminancemeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_ILLUMINANCEMEASURMENT_MEASUREDVALUE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_ILLUMINANCEMEASURMENT_MEASUREDVALUE); + public static final String CHANNEL_ID_MODESELECT_MODE = "modeselect-mode"; + public static final ChannelTypeUID CHANNEL_MODESELECT_MODE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_MODESELECT_MODE); + public static final String CHANNEL_ID_BOOLEANSTATE_STATEVALUE = "booleanstate-statevalue"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATE_STATEVALUE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_BOOLEANSTATE_STATEVALUE); + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI = "wifinetworkdiagnostics-rssi"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_RSSI = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI); + public static final String CHANNEL_ID_SWITCH_SWITCH = "switch-switch"; + public static final ChannelTypeUID CHANNEL_SWITCH_SWITCH = new ChannelTypeUID(BINDING_ID, CHANNEL_ID_SWITCH_SWITCH); + public static final String CHANNEL_ID_SWITCH_SWITCHLATECHED = "switch-switchlatched"; + public static final ChannelTypeUID CHANNEL_SWITCH_SWITCHLATECHED = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_SWITCHLATECHED); + public static final String CHANNEL_ID_SWITCH_INITIALPRESS = "switch-initialpress"; + public static final ChannelTypeUID CHANNEL_SWITCH_INITIALPRESS = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_INITIALPRESS); + public static final String CHANNEL_ID_SWITCH_LONGPRESS = "switch-longpress"; + public static final ChannelTypeUID CHANNEL_SWITCH_LONGPRESS = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_LONGPRESS); + public static final String CHANNEL_ID_SWITCH_SHORTRELEASE = "switch-shortrelease"; + public static final ChannelTypeUID CHANNEL_SWITCH_SHORTRELEASE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_SHORTRELEASE); + public static final String CHANNEL_ID_SWITCH_LONGRELEASE = "switch-longrelease"; + public static final ChannelTypeUID CHANNEL_SWITCH_LONGRELEASE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_LONGRELEASE); + public static final String CHANNEL_ID_SWITCH_MULTIPRESSONGOING = "switch-multipressongoing"; + public static final ChannelTypeUID CHANNEL_SWITCH_MULTIPRESSONGOING = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_MULTIPRESSONGOING); + public static final String CHANNEL_ID_SWITCH_MULTIPRESSCOMPLETE = "switch-multipresscomplete"; + public static final ChannelTypeUID CHANNEL_SWITCH_MULTIPRESSCOMPLETE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_SWITCH_MULTIPRESSCOMPLETE); + // shared by energy imported and exported + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_ENERGYMEASUREMENT_ENERGY = new ChannelTypeUID( + BINDING_ID, "electricalenergymeasurement-energymeasurmement-energy"); + + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED_ENERGY = "electricalenergymeasurement-cumulativeenergyimported-energy"; + + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED_ENERGY = "electricalenergymeasurement-cumulativeenergyexported-energy"; + + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED_ENERGY = "electricalenergymeasurement-periodicenergyimported-energy"; + + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED_ENERGY = "electricalenergymeasurement-periodicenergyexported-energy"; + + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE = "electricalpowermeasurement-voltage"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_VOLTAGE = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE); + + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT = "electricalpowermeasurement-activecurrent"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT); + + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER = "electricalpowermeasurement-activepower"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER = new ChannelTypeUID(BINDING_ID, + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER); + + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME = "threadborderroutermgmt-borderroutername"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID = "threadborderroutermgmt-borderagentid"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_THREADVERSION = "threadborderroutermgmt-threadversion"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED = "threadborderroutermgmt-interfaceenabled"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP = "threadborderroutermgmt-activedatasettimestamp"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP = "threadborderroutermgmt-pendingdatasettimestamp"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASET = "threadborderroutermgmt-activedataset"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_PENDINGDATASET = "threadborderroutermgmt-pendingdataset"; + + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_THREADVERSION = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_THREADVERSION); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASET = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASET); + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_PENDINGDATASET = new ChannelTypeUID( + BINDING_ID, CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_PENDINGDATASET); + + // Thread Border Router Configuration Keys + public static final String CONFIG_THREAD_CHANNEL = "channel"; + public static final String CONFIG_THREAD_ALLOWED_CHANNELS = "allowedChannels"; + public static final String CONFIG_THREAD_EXTENDED_PAN_ID = "extendedPanId"; + public static final String CONFIG_THREAD_MESH_LOCAL_PREFIX = "meshLocalPrefix"; + public static final String CONFIG_THREAD_NETWORK_NAME = "networkName"; + public static final String CONFIG_THREAD_NETWORK_KEY = "networkKey"; + public static final String CONFIG_THREAD_PAN_ID = "panId"; + public static final String CONFIG_THREAD_PSKC = "pskc"; + public static final String CONFIG_THREAD_ACTIVE_TIMESTAMP_SECONDS = "activeTimestampSeconds"; + public static final String CONFIG_THREAD_ACTIVE_TIMESTAMP_TICKS = "activeTimestampTicks"; + public static final String CONFIG_THREAD_ACTIVE_TIMESTAMP_AUTHORITATIVE = "activeTimestampAuthoritative"; + public static final String CONFIG_THREAD_DELAY_TIMER = "delayTimer"; + public static final String CONFIG_THREAD_ROTATION_TIME = "rotationTime"; + public static final String CONFIG_THREAD_OBTAIN_NETWORK_KEY = "obtainNetworkKey"; + public static final String CONFIG_THREAD_NATIVE_COMMISSIONING = "nativeCommissioning"; + public static final String CONFIG_THREAD_ROUTERS = "routers"; + public static final String CONFIG_THREAD_EXTERNAL_COMMISSIONING = "externalCommissioning"; + public static final String CONFIG_THREAD_COMMERCIAL_COMMISSIONING = "commercialCommissioning"; + public static final String CONFIG_THREAD_AUTONOMOUS_ENROLLMENT = "autonomousEnrollment"; + public static final String CONFIG_THREAD_NETWORK_KEY_PROVISIONING = "networkKeyProvisioning"; + public static final String CONFIG_THREAD_TOBLE_LINK = "tobleLink"; + public static final String CONFIG_THREAD_NON_CCM_ROUTERS = "nonCcmRouters"; + + // Thread Border Router Configuration Labels + public static final String CONFIG_LABEL_THREAD_BORDER_ROUTER_OPERATIONAL_DATASET = "@text/thing-type.config.matter.node.thread_border_router_operational_dataset.label"; + public static final String CONFIG_LABEL_THREAD_NETWORK_CHANNEL_NUMBER = "@text/thing-type.config.matter.node.thread_network_channel_number.label"; + public static final String CONFIG_LABEL_THREAD_NETWORK_ALLOWED_CHANNELS = "@text/thing-type.config.matter.node.thread_network_allowed_channels.label"; + public static final String CONFIG_LABEL_THREAD_EXTENDED_PAN_ID = "@text/thing-type.config.matter.node.thread_extended_pan_id.label"; + public static final String CONFIG_LABEL_THREAD_MESH_LOCAL_PREFIX = "@text/thing-type.config.matter.node.thread_mesh_local_prefix.label"; + public static final String CONFIG_LABEL_THREAD_NETWORK_NAME = "@text/thing-type.config.matter.node.thread_network_name.label"; + public static final String CONFIG_LABEL_THREAD_NETWORK_KEY = "@text/thing-type.config.matter.node.thread_network_key.label"; + public static final String CONFIG_LABEL_THREAD_PAN_ID = "@text/thing-type.config.matter.node.thread_pan_id.label"; + public static final String CONFIG_LABEL_THREAD_PSKC = "@text/thing-type.config.matter.node.thread_pskc.label"; + public static final String CONFIG_LABEL_THREAD_ACTIVE_TIMESTAMP_SECONDS = "@text/thing-type.config.matter.node.thread_active_timestamp_seconds.label"; + public static final String CONFIG_LABEL_THREAD_ACTIVE_TIMESTAMP_TICKS = "@text/thing-type.config.matter.node.thread_active_timestamp_ticks.label"; + public static final String CONFIG_LABEL_THREAD_ACTIVE_TIMESTAMP_IS_AUTHORITATIVE = "@text/thing-type.config.matter.node.thread_active_timestamp_is_authoritative.label"; + public static final String CONFIG_LABEL_THREAD_DATASET_SECURITY_POLICY = "@text/thing-type.config.matter.node.thread_dataset_security_policy.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_ROTATION_TIME = "@text/thing-type.config.matter.node.security_policy_rotation_time.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_OBTAIN_NETWORK_KEY = "@text/thing-type.config.matter.node.security_policy_obtain_network_key.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_NATIVE_COMMISSIONING = "@text/thing-type.config.matter.node.security_policy_native_commissioning.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_ROUTERS = "@text/thing-type.config.matter.node.security_policy_routers.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_EXTERNAL_COMMISSIONING = "@text/thing-type.config.matter.node.security_policy_external_commissioning.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_COMMERCIAL_COMMISSIONING = "@text/thing-type.config.matter.node.security_policy_commercial_commissioning.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_AUTONOMOUS_ENROLLMENT = "@text/thing-type.config.matter.node.security_policy_autonomous_enrollment.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_NETWORK_KEY_PROVISIONING = "@text/thing-type.config.matter.node.security_policy_network_key_provisioning.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_TO_BLE_LINK = "@text/thing-type.config.matter.node.security_policy_to_ble_link.label"; + public static final String CONFIG_LABEL_SECURITY_POLICY_NON_CCM_ROUTERS = "@text/thing-type.config.matter.node.security_policy_non_ccm_routers.label"; + + // Thread Border Router Configuration Descriptions + public static final String CONFIG_DESC_THREAD_BORDER_ROUTER_OPERATIONAL_DATASET = "@text/thing-type.config.matter.node.thread_border_router_operational_dataset.description"; + public static final String CONFIG_DESC_THREAD_NETWORK_CHANNEL_NUMBER = "@text/thing-type.config.matter.node.thread_network_channel_number.description"; + public static final String CONFIG_DESC_THREAD_NETWORK_ALLOWED_CHANNELS = "@text/thing-type.config.matter.node.thread_network_allowed_channels.description"; + public static final String CONFIG_DESC_THREAD_EXTENDED_PAN_ID = "@text/thing-type.config.matter.node.thread_extended_pan_id.description"; + public static final String CONFIG_DESC_THREAD_MESH_LOCAL_PREFIX = "@text/thing-type.config.matter.node.thread_mesh_local_prefix.description"; + public static final String CONFIG_DESC_THREAD_NETWORK_NAME = "@text/thing-type.config.matter.node.thread_network_name.description"; + public static final String CONFIG_DESC_THREAD_NETWORK_KEY = "@text/thing-type.config.matter.node.thread_network_key.description"; + public static final String CONFIG_DESC_THREAD_PAN_ID = "@text/thing-type.config.matter.node.thread_pan_id.description"; + public static final String CONFIG_DESC_THREAD_PSKC = "@text/thing-type.config.matter.node.thread_pskc.description"; + public static final String CONFIG_DESC_THREAD_ACTIVE_TIMESTAMP_SECONDS = "@text/thing-type.config.matter.node.thread_active_timestamp_seconds.description"; + public static final String CONFIG_DESC_THREAD_ACTIVE_TIMESTAMP_TICKS = "@text/thing-type.config.matter.node.thread_active_timestamp_ticks.description"; + public static final String CONFIG_DESC_THREAD_ACTIVE_TIMESTAMP_IS_AUTHORITATIVE = "@text/thing-type.config.matter.node.thread_active_timestamp_is_authoritative.description"; + public static final String CONFIG_DESC_THREAD_DATASET_SECURITY_POLICY = "@text/thing-type.config.matter.node.thread_dataset_security_policy.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_ROTATION_TIME = "@text/thing-type.config.matter.node.security_policy_rotation_time.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_OBTAIN_NETWORK_KEY = "@text/thing-type.config.matter.node.security_policy_obtain_network_key.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_NATIVE_COMMISSIONING = "@text/thing-type.config.matter.node.security_policy_native_commissioning.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_ROUTERS = "@text/thing-type.config.matter.node.security_policy_routers.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_EXTERNAL_COMMISSIONING = "@text/thing-type.config.matter.node.security_policy_external_commissioning.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_COMMERCIAL_COMMISSIONING = "@text/thing-type.config.matter.node.security_policy_commercial_commissioning.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_AUTONOMOUS_ENROLLMENT = "@text/thing-type.config.matter.node.security_policy_autonomous_enrollment.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_NETWORK_KEY_PROVISIONING = "@text/thing-type.config.matter.node.security_policy_network_key_provisioning.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_TO_BLE_LINK = "@text/thing-type.config.matter.node.security_policy_to_ble_link.description"; + public static final String CONFIG_DESC_SECURITY_POLICY_NON_CCM_ROUTERS = "@text/thing-type.config.matter.node.security_policy_non_ccm_routers.description"; + // Thread Border Router Configuration Group Names + public static final String CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE = "threadBorderRouterCore"; + public static final String CONFIG_GROUP_SECURITY_POLICY = "securityPolicy"; + + // Matter Node Actions + public static final String THING_ACTION_LABEL_NODE_GENERATE_NEW_PAIRING_CODE = "@text/thing-action.node.generateNewPairingCode.label"; + public static final String THING_ACTION_DESC_NODE_GENERATE_NEW_PAIRING_CODE = "@text/thing-action.node.generateNewPairingCode.description"; + public static final String THING_ACTION_LABEL_NODE_GENERATE_NEW_PAIRING_CODE_MANUAL_PAIRING_CODE = "@text/thing-action.node.generateNewPairingCode.manual-pairing-code.label"; + public static final String THING_ACTION_LABEL_NODE_GENERATE_NEW_PAIRING_CODE_QR_PAIRING_CODE = "@text/thing-action.node.generateNewPairingCode.qr-pairing-code.label"; + public static final String THING_ACTION_LABEL_NODE_DECOMMISSION = "@text/thing-action.node.decommission.label"; + public static final String THING_ACTION_DESC_NODE_DECOMMISSION = "@text/thing-action.node.decommission.description"; + public static final String THING_ACTION_LABEL_NODE_DECOMMISSION_RESULT = "@text/thing-action.node.decommission.result.label"; + public static final String THING_ACTION_LABEL_NODE_GET_FABRICS = "@text/thing-action.node.getFabrics.label"; + public static final String THING_ACTION_DESC_NODE_GET_FABRICS = "@text/thing-action.node.getFabrics.description"; + public static final String THING_ACTION_LABEL_NODE_GET_FABRICS_RESULT = "@text/thing-action.node.getFabrics.result.label"; + public static final String THING_ACTION_LABEL_NODE_REMOVE_FABRIC = "@text/thing-action.node.removeFabric.label"; + public static final String THING_ACTION_DESC_NODE_REMOVE_FABRIC = "@text/thing-action.node.removeFabric.description"; + public static final String THING_ACTION_LABEL_NODE_REMOVE_FABRIC_RESULT = "@text/thing-action.node.removeFabric.result.label"; + public static final String THING_ACTION_LABEL_NODE_REMOVE_FABRIC_INDEX = "@text/thing-action.node.removeFabric.index.label"; + public static final String THING_ACTION_DESC_NODE_REMOVE_FABRIC_INDEX = "@text/thing-action.node.removeFabric.index.description"; + + // Action Result Messages + public static final String THING_ACTION_RESULT_SUCCESS = "@text/thing-action.result.success"; + public static final String THING_ACTION_RESULT_NO_HANDLER = "@text/thing-action.result.no-handler"; + public static final String THING_ACTION_RESULT_NO_FABRICS = "@text/thing-action.result.no-fabrics"; + + // Matter OTBR Actions + public static final String THING_ACTION_LABEL_OTBR_LOAD_EXTERNAL_DATASET = "@text/thing-action.otbr.loadExternalDataset.label"; + public static final String THING_ACTION_DESC_OTBR_LOAD_EXTERNAL_DATASET = "@text/thing-action.otbr.loadExternalDataset.description"; + public static final String THING_ACTION_LABEL_OTBR_LOAD_EXTERNAL_DATASET_DATASET = "@text/thing-action.otbr.loadExternalDataset.dataset.label"; + public static final String THING_ACTION_DESC_OTBR_LOAD_EXTERNAL_DATASET_DATASET = "@text/thing-action.otbr.loadExternalDataset.dataset.description"; + public static final String THING_ACTION_LABEL_OTBR_LOAD_EXTERNAL_DATASET_RESULT = "@text/thing-action.otbr.loadExternalDataset.result.label"; + + public static final String THING_ACTION_LABEL_OTBR_LOAD_ACTIVE_DATASET = "@text/thing-action.otbr.loadActiveDataset.label"; + public static final String THING_ACTION_DESC_OTBR_LOAD_ACTIVE_DATASET = "@text/thing-action.otbr.loadActiveDataset.description"; + public static final String THING_ACTION_LABEL_OTBR_LOAD_ACTIVE_DATASET_RESULT = "@text/thing-action.otbr.loadActiveDataset.result.label"; + public static final String THING_ACTION_LABEL_OTBR_LOAD_ACTIVE_DATASET_DATASET = "@text/thing-action.otbr.loadActiveDataset.dataset.label"; + + public static final String THING_ACTION_LABEL_OTBR_PUSH_DATASET = "@text/thing-action.otbr.pushDataset.label"; + public static final String THING_ACTION_DESC_OTBR_PUSH_DATASET = "@text/thing-action.otbr.pushDataset.description"; + public static final String THING_ACTION_LABEL_OTBR_PUSH_DATASET_RESULT = "@text/thing-action.otbr.pushDataset.result.label"; + public static final String THING_ACTION_LABEL_OTBR_PUSH_DATASET_DELAY = "@text/thing-action.otbr.pushDataset.delay.label"; + public static final String THING_ACTION_DESC_OTBR_PUSH_DATASET_DELAY = "@text/thing-action.otbr.pushDataset.delay.description"; + public static final String THING_ACTION_LABEL_OTBR_PUSH_DATASET_GENERATE_TIME = "@text/thing-action.otbr.pushDataset.generateTime.label"; + public static final String THING_ACTION_DESC_OTBR_PUSH_DATASET_GENERATE_TIME = "@text/thing-action.otbr.pushDataset.generateTime.description"; + public static final String THING_ACTION_LABEL_OTBR_PUSH_DATASET_INCREMENT_TIME = "@text/thing-action.otbr.pushDataset.incrementTime.label"; + public static final String THING_ACTION_DESC_OTBR_PUSH_DATASET_INCREMENT_TIME = "@text/thing-action.otbr.pushDataset.incrementTime.description"; + + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET = "@text/thing-action.otbr.generateDataset.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET = "@text/thing-action.otbr.generateDataset.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_RESULT = "@text/thing-action.otbr.generateDataset.result.label"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_JSON = "@text/thing-action.otbr.generateDataset.json.label"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_HEX = "@text/thing-action.otbr.generateDataset.hex.label"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_SAVE = "@text/thing-action.otbr.generateDataset.save.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_SAVE = "@text/thing-action.otbr.generateDataset.save.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_CHANNEL = "@text/thing-action.otbr.generateDataset.channel.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_CHANNEL = "@text/thing-action.otbr.generateDataset.channel.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TIMESTAMP_SECONDS = "@text/thing-action.otbr.generateDataset.timestampSeconds.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_TIMESTAMP_SECONDS = "@text/thing-action.otbr.generateDataset.timestampSeconds.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TIMESTAMP_TICKS = "@text/thing-action.otbr.generateDataset.timestampTicks.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_TIMESTAMP_TICKS = "@text/thing-action.otbr.generateDataset.timestampTicks.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TIMESTAMP_AUTHORITATIVE = "@text/thing-action.otbr.generateDataset.timestampAuthoritative.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_TIMESTAMP_AUTHORITATIVE = "@text/thing-action.otbr.generateDataset.timestampAuthoritative.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_PAN_ID = "@text/thing-action.otbr.generateDataset.panId.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_PAN_ID = "@text/thing-action.otbr.generateDataset.panId.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_EXTENDED_PAN_ID = "@text/thing-action.otbr.generateDataset.extendedPanId.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_EXTENDED_PAN_ID = "@text/thing-action.otbr.generateDataset.extendedPanId.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_MESH_PREFIX = "@text/thing-action.otbr.generateDataset.meshPrefix.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_MESH_PREFIX = "@text/thing-action.otbr.generateDataset.meshPrefix.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NETWORK_NAME = "@text/thing-action.otbr.generateDataset.networkName.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_NETWORK_NAME = "@text/thing-action.otbr.generateDataset.networkName.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NETWORK_KEY = "@text/thing-action.otbr.generateDataset.networkKey.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_NETWORK_KEY = "@text/thing-action.otbr.generateDataset.networkKey.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_PASSPHRASE = "@text/thing-action.otbr.generateDataset.passphrase.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_PASSPHRASE = "@text/thing-action.otbr.generateDataset.passphrase.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_ROTATION_TIME = "@text/thing-action.otbr.generateDataset.rotationTime.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_ROTATION_TIME = "@text/thing-action.otbr.generateDataset.rotationTime.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_OBTAIN_NETWORK_KEY = "@text/thing-action.otbr.generateDataset.obtainNetworkKey.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_OBTAIN_NETWORK_KEY = "@text/thing-action.otbr.generateDataset.obtainNetworkKey.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NATIVE_COMMISSIONING = "@text/thing-action.otbr.generateDataset.nativeCommissioning.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_NATIVE_COMMISSIONING = "@text/thing-action.otbr.generateDataset.nativeCommissioning.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_ROUTERS = "@text/thing-action.otbr.generateDataset.routers.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_ROUTERS = "@text/thing-action.otbr.generateDataset.routers.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_EXTERNAL_COMMISSIONING = "@text/thing-action.otbr.generateDataset.externalCommissioning.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_EXTERNAL_COMMISSIONING = "@text/thing-action.otbr.generateDataset.externalCommissioning.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_COMMERCIAL_COMMISSIONING = "@text/thing-action.otbr.generateDataset.commercialCommissioning.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_COMMERCIAL_COMMISSIONING = "@text/thing-action.otbr.generateDataset.commercialCommissioning.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_AUTONOMOUS_ENROLLMENT = "@text/thing-action.otbr.generateDataset.autonomousEnrollment.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_AUTONOMOUS_ENROLLMENT = "@text/thing-action.otbr.generateDataset.autonomousEnrollment.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NETWORK_KEY_PROVISIONING = "@text/thing-action.otbr.generateDataset.networkKeyProvisioning.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_NETWORK_KEY_PROVISIONING = "@text/thing-action.otbr.generateDataset.networkKeyProvisioning.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TOBLE_LINK = "@text/thing-action.otbr.generateDataset.tobleLink.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_TOBLE_LINK = "@text/thing-action.otbr.generateDataset.tobleLink.description"; + public static final String THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NON_CCM_ROUTERS = "@text/thing-action.otbr.generateDataset.nonCcmRouters.label"; + public static final String THING_ACTION_DESC_OTBR_GENERATE_DATASET_NON_CCM_ROUTERS = "@text/thing-action.otbr.generateDataset.nonCcmRouters.description"; + + // Matter OTBR Action Results + public static final String THING_ACTION_RESULT_NO_CONVERTER = "@text/thing-action.result.no-converter"; + public static final String THING_ACTION_RESULT_INVALID_JSON = "@text/thing-action.result.invalid-json"; + public static final String THING_ACTION_RESULT_ERROR_GENERATING_KEY = "@text/thing-action.result.error-generating-key"; + public static final String THING_ACTION_RESULT_ERROR_SETTING_DATASET = "@text/thing-action.result.error-setting-dataset"; + + // Matter Controller Actions + public static final String THING_ACTION_LABEL_CONTROLLER_PAIR_DEVICE = "@text/thing-action.controller.pairDevice.label"; + public static final String THING_ACTION_DESC_CONTROLLER_PAIR_DEVICE = "@text/thing-action.controller.pairDevice.description"; + public static final String THING_ACTION_LABEL_CONTROLLER_PAIR_DEVICE_CODE = "@text/thing-action.controller.pairDevice.code.label"; + public static final String THING_ACTION_DESC_CONTROLLER_PAIR_DEVICE_CODE = "@text/thing-action.controller.pairDevice.code.description"; + public static final String THING_ACTION_LABEL_CONTROLLER_PAIR_DEVICE_RESULT = "@text/thing-action.controller.pairDevice.result.label"; + public static final String THING_ACTION_RESULT_DEVICE_ADDED = "@text/thing-action.result.device-added"; + public static final String THING_ACTION_RESULT_PAIRING_FAILED = "@text/thing-action.result.pairing-failed"; + + // Matter Controller Statuses + public static final String THING_STATUS_DETAIL_CONTROLLER_WAITING_FOR_DATA = "@text/thing-status.detail.controller.waitingForData"; + public static final String THING_STATUS_DETAIL_ENDPOINT_THING_NOT_REACHABLE = "@text/thing-status.detail.endpoint.thingNotReachable"; + + // Discovery + public static final String DISCOVERY_MATTER_BRIDGE_ENDPOINT_LABEL = "@text/discovery.matter.bridge-endpoint.label"; + public static final String DISCOVERY_MATTER_NODE_DEVICE_LABEL = "@text/discovery.matter.node-device.label"; + public static final String DISCOVERY_MATTER_UNKNOWN_NODE_LABEL = "@text/discovery.matter.unknown-node.label"; + public static final String DISCOVERY_MATTER_SCAN_INPUT_LABEL = "@text/discovery.matter.scan-input.label"; + public static final String DISCOVERY_MATTER_SCAN_INPUT_DESCRIPTION = "@text/discovery.matter.scan-input.description"; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterChannelTypeProvider.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterChannelTypeProvider.java new file mode 100644 index 00000000000..90742f38766 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterChannelTypeProvider.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal; + +import java.net.URI; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.storage.StorageService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.AbstractStorageBasedTypeProvider; +import org.openhab.core.thing.binding.ThingTypeProvider; +import org.openhab.core.thing.type.ChannelGroupType; +import org.openhab.core.thing.type.ChannelGroupTypeProvider; +import org.openhab.core.thing.type.ChannelGroupTypeUID; +import org.openhab.core.thing.type.ChannelTypeProvider; +import org.openhab.core.thing.type.ThingType; +import org.openhab.core.thing.type.ThingTypeBuilder; +import org.openhab.core.thing.type.ThingTypeRegistry; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * This class is used to provide channel types dynamically for Matter devices. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(immediate = false, service = { ThingTypeProvider.class, ChannelTypeProvider.class, + ChannelGroupTypeProvider.class, MatterChannelTypeProvider.class }) +public class MatterChannelTypeProvider extends AbstractStorageBasedTypeProvider { + private final ThingTypeRegistry thingTypeRegistry; + + @Activate + public MatterChannelTypeProvider(@Reference ThingTypeRegistry thingTypeRegistry, + @Reference StorageService storageService) { + super(storageService); + this.thingTypeRegistry = thingTypeRegistry; + } + + /** + * Clone the defaults from a XML defined thing (baseType). Optionally pass in bridgeTypes of parent things that are + * dynamic and not defined in xml + * + */ + public ThingTypeBuilder derive(ThingTypeUID newTypeId, ThingTypeUID baseTypeId, + @Nullable List supportedBridgeTypeUIDs) { + ThingType baseType = thingTypeRegistry.getThingType(baseTypeId); + + if (baseType == null) { + throw new IllegalArgumentException("Base type not found: " + baseTypeId); + } + + ThingTypeBuilder result = ThingTypeBuilder.instance(newTypeId, baseType.getLabel()) + .withChannelGroupDefinitions(baseType.getChannelGroupDefinitions()) + .withChannelDefinitions(baseType.getChannelDefinitions()) + .withExtensibleChannelTypeIds(baseType.getExtensibleChannelTypeIds()) + .withSupportedBridgeTypeUIDs(supportedBridgeTypeUIDs != null ? supportedBridgeTypeUIDs + : baseType.getSupportedBridgeTypeUIDs()) + .withProperties(baseType.getProperties()).isListed(false); + + String representationProperty = baseType.getRepresentationProperty(); + if (representationProperty != null) { + result = result.withRepresentationProperty(representationProperty); + } + URI configDescriptionURI = baseType.getConfigDescriptionURI(); + if (configDescriptionURI != null) { + result = result.withConfigDescriptionURI(configDescriptionURI); + } + String category = baseType.getCategory(); + if (category != null) { + result = result.withCategory(category); + } + String description = baseType.getDescription(); + if (description != null) { + result = result.withDescription(description); + } + + return result; + } + + public void updateChannelGroupTypesForPrefix(String prefix, Collection types) { + Collection oldCgts = channelGroupTypesForPrefix(prefix); + + Set oldUids = oldCgts.stream().map(ChannelGroupType::getUID).collect(Collectors.toSet()); + Collection uids = types.stream().map(ChannelGroupType::getUID).toList(); + + oldUids.removeAll(uids); + // oldUids now contains only UIDs that no longer exist. so remove them + oldUids.forEach(this::removeChannelGroupType); + types.forEach(this::putChannelGroupType); + } + + public void removeChannelGroupTypesForPrefix(String prefix) { + channelGroupTypesForPrefix(prefix).forEach(cgt -> removeChannelGroupType(cgt.getUID())); + } + + private Collection channelGroupTypesForPrefix(String prefix) { + return getChannelGroupTypes(null).stream().filter(cgt -> cgt.getUID().getId().startsWith(prefix + "-")) + .toList(); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterConfigDescriptionProvider.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterConfigDescriptionProvider.java new file mode 100644 index 00000000000..6d8ef8296ce --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterConfigDescriptionProvider.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal; + +import java.net.URI; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.config.core.ConfigDescription; +import org.openhab.core.config.core.ConfigDescriptionProvider; +import org.osgi.service.component.annotations.Component; + +/** + * Extends the ConfigDescriptionProvider to dynamically add ConfigDescriptions. + * + * @author Dan Cunningham - Initial contribution + */ +@Component(service = { MatterConfigDescriptionProvider.class, ConfigDescriptionProvider.class }) +@NonNullByDefault +public class MatterConfigDescriptionProvider implements ConfigDescriptionProvider { + + private Map configDescriptionsByURI = new HashMap<>(); + + @Override + public Collection getConfigDescriptions(@Nullable Locale locale) { + return new ArrayList(configDescriptionsByURI.values()); + } + + @Override + @Nullable + public ConfigDescription getConfigDescription(URI uri, @Nullable Locale locale) { + return configDescriptionsByURI.get(uri); + } + + public void addConfigDescription(ConfigDescription configDescription) { + configDescriptionsByURI.put(configDescription.getUID(), configDescription); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterHandlerFactory.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterHandlerFactory.java new file mode 100644 index 00000000000..a450130d532 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterHandlerFactory.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.THING_TYPE_CONTROLLER; +import static org.openhab.binding.matter.internal.MatterBindingConstants.THING_TYPE_ENDPOINT; +import static org.openhab.binding.matter.internal.MatterBindingConstants.THING_TYPE_NODE; + +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.MatterWebsocketService; +import org.openhab.binding.matter.internal.handler.ControllerHandler; +import org.openhab.binding.matter.internal.handler.EndpointHandler; +import org.openhab.binding.matter.internal.handler.NodeHandler; +import org.openhab.binding.matter.internal.util.MatterUIDUtils; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * The {@link MatterHandlerFactory} is responsible for creating things and thing + * handlers. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(service = { ThingHandlerFactory.class, MatterHandlerFactory.class }) +public class MatterHandlerFactory extends BaseThingHandlerFactory { + private static final Set SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_CONTROLLER, THING_TYPE_NODE, + THING_TYPE_ENDPOINT); + + private final MatterStateDescriptionOptionProvider stateDescriptionProvider; + private final MatterWebsocketService websocketService; + private final MatterChannelTypeProvider channelGroupTypeProvider; + private final MatterConfigDescriptionProvider configDescriptionProvider; + private final TranslationService translationService; + + @Activate + public MatterHandlerFactory(@Reference MatterWebsocketService websocketService, + @Reference MatterStateDescriptionOptionProvider stateDescriptionProvider, + @Reference MatterChannelTypeProvider channelGroupTypeProvider, + @Reference MatterConfigDescriptionProvider configDescriptionProvider, + @Reference TranslationService translationService) { + this.websocketService = websocketService; + this.stateDescriptionProvider = stateDescriptionProvider; + this.channelGroupTypeProvider = channelGroupTypeProvider; + this.configDescriptionProvider = configDescriptionProvider; + this.translationService = translationService; + } + + @Override + public boolean supportsThingType(ThingTypeUID thingTypeUID) { + ThingTypeUID baseTypeUID = MatterUIDUtils.baseTypeForThingType(thingTypeUID); + return SUPPORTED_THING_TYPES_UIDS.contains(baseTypeUID != null ? baseTypeUID : thingTypeUID); + } + + @Override + protected @Nullable ThingHandler createHandler(Thing thing) { + ThingTypeUID thingTypeUID = thing.getThingTypeUID(); + + if (THING_TYPE_CONTROLLER.equals(thingTypeUID)) { + ControllerHandler controllerHandler = new ControllerHandler((Bridge) thing, websocketService, + translationService); + return controllerHandler; + } + + ThingTypeUID baseTypeUID = MatterUIDUtils.baseTypeForThingType(thingTypeUID); + ThingTypeUID derivedTypeUID = baseTypeUID != null ? baseTypeUID : thingTypeUID; + + if (THING_TYPE_NODE.equals(derivedTypeUID)) { + return new NodeHandler((Bridge) thing, this, stateDescriptionProvider, channelGroupTypeProvider, + configDescriptionProvider, translationService); + } + + if (THING_TYPE_ENDPOINT.equals(derivedTypeUID)) { + return new EndpointHandler(thing, this, stateDescriptionProvider, channelGroupTypeProvider, + configDescriptionProvider, translationService); + } + + return null; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterStateDescriptionOptionProvider.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterStateDescriptionOptionProvider.java new file mode 100644 index 00000000000..682dc21464e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/MatterStateDescriptionOptionProvider.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal; + +import java.math.BigDecimal; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.events.EventPublisher; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.BaseDynamicStateDescriptionProvider; +import org.openhab.core.thing.events.ThingEventFactory; +import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService; +import org.openhab.core.thing.link.ItemChannelLinkRegistry; +import org.openhab.core.thing.type.DynamicStateDescriptionProvider; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragment; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +/** + * Dynamic provider of state options + * + * @author Dan Cunningham - Initial contribution + */ +@Component(service = { DynamicStateDescriptionProvider.class, MatterStateDescriptionOptionProvider.class }) +@NonNullByDefault +public class MatterStateDescriptionOptionProvider extends BaseDynamicStateDescriptionProvider { + + private final Map stateDescriptionFragments = new ConcurrentHashMap<>(); + + @Activate + public MatterStateDescriptionOptionProvider(final @Reference EventPublisher eventPublisher, // + final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, // + final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) { + this.eventPublisher = eventPublisher; + this.itemChannelLinkRegistry = itemChannelLinkRegistry; + this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService; + } + + @Override + public @Nullable StateDescription getStateDescription(Channel channel, @Nullable StateDescription original, + @Nullable Locale locale) { + StateDescriptionFragment stateDescriptionFragment = stateDescriptionFragments.get(channel.getUID()); + return stateDescriptionFragment != null ? stateDescriptionFragment.toStateDescription() + : super.getStateDescription(channel, original, locale); + } + + public void setMinMax(ChannelUID channelUID, BigDecimal min, BigDecimal max, @Nullable BigDecimal step, + @Nullable String pattern) { + StateDescriptionFragment oldStateDescriptionFragment = stateDescriptionFragments.get(channelUID); + StateDescriptionFragmentBuilder builder = StateDescriptionFragmentBuilder.create().withMinimum(min) + .withMaximum(max); + if (step != null) { + builder = builder.withStep(step); + } + if (pattern != null) { + builder = builder.withPattern(pattern); + } + StateDescriptionFragment newStateDescriptionFragment = builder.build(); + if (!newStateDescriptionFragment.equals(oldStateDescriptionFragment)) { + stateDescriptionFragments.put(channelUID, newStateDescriptionFragment); + ItemChannelLinkRegistry itemChannelLinkRegistry = this.itemChannelLinkRegistry; + postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID, + itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(), + newStateDescriptionFragment, oldStateDescriptionFragment)); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterControllerActions.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterControllerActions.java new file mode 100644 index 00000000000..9adfe01a8ef --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterControllerActions.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.actions; + +import java.util.concurrent.ExecutionException; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterBindingConstants; +import org.openhab.binding.matter.internal.handler.ControllerHandler; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.ActionOutput; +import org.openhab.core.automation.annotation.ActionOutputs; +import org.openhab.core.automation.annotation.RuleAction; +import org.openhab.core.thing.binding.ThingActions; +import org.openhab.core.thing.binding.ThingActionsScope; +import org.openhab.core.thing.binding.ThingHandler; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link MatterControllerActions} exposes Matter related actions for the Matter Controller Thing. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(scope = ServiceScope.PROTOTYPE, service = MatterControllerActions.class) +@ThingActionsScope(name = "matter") +public class MatterControllerActions implements ThingActions { + public final Logger logger = LoggerFactory.getLogger(getClass()); + private @Nullable ControllerHandler handler; + private final TranslationService translationService; + + @Activate + public MatterControllerActions(@Reference TranslationService translationService) { + this.translationService = translationService; + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof ControllerHandler controllerHandler) { + this.handler = controllerHandler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_CONTROLLER_PAIR_DEVICE, description = MatterBindingConstants.THING_ACTION_DESC_CONTROLLER_PAIR_DEVICE) + public @Nullable @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_CONTROLLER_PAIR_DEVICE_RESULT, type = "java.lang.String") }) String pairDevice( + @ActionInput(name = "code", label = MatterBindingConstants.THING_ACTION_LABEL_CONTROLLER_PAIR_DEVICE_CODE, description = MatterBindingConstants.THING_ACTION_DESC_CONTROLLER_PAIR_DEVICE_CODE, type = "java.lang.String") String code) { + ControllerHandler handler = this.handler; + if (handler != null) { + try { + handler.startScan(code).get(); + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_DEVICE_ADDED); + } catch (InterruptedException | ExecutionException e) { + return handler.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_PAIRING_FAILED) + + e.getLocalizedMessage(); + } + } + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterNodeActions.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterNodeActions.java new file mode 100644 index 00000000000..f8c34d9f1a4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterNodeActions.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.actions; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterBindingConstants; +import org.openhab.binding.matter.internal.client.dto.PairingCodes; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OperationalCredentialsCluster; +import org.openhab.binding.matter.internal.controller.MatterControllerClient; +import org.openhab.binding.matter.internal.handler.NodeHandler; +import org.openhab.binding.matter.internal.util.MatterVendorIDs; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.ActionOutput; +import org.openhab.core.automation.annotation.ActionOutputs; +import org.openhab.core.automation.annotation.RuleAction; +import org.openhab.core.thing.binding.ThingActions; +import org.openhab.core.thing.binding.ThingActionsScope; +import org.openhab.core.thing.binding.ThingHandler; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonParseException; + +/** + * The {@link MatterNodeActions} exposes Matter related actions for the Matter Node Thing. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(scope = ServiceScope.PROTOTYPE, service = MatterNodeActions.class) +@ThingActionsScope(name = "matter") +public class MatterNodeActions implements ThingActions { + public final Logger logger = LoggerFactory.getLogger(getClass()); + protected @Nullable NodeHandler handler; + private final TranslationService translationService; + + @Activate + public MatterNodeActions(@Reference TranslationService translationService) { + this.translationService = translationService; + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof NodeHandler nodeHandler) { + this.handler = nodeHandler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_NODE_GENERATE_NEW_PAIRING_CODE, description = MatterBindingConstants.THING_ACTION_DESC_NODE_GENERATE_NEW_PAIRING_CODE) + public @ActionOutputs({ + @ActionOutput(name = "manualPairingCode", label = MatterBindingConstants.THING_ACTION_LABEL_NODE_GENERATE_NEW_PAIRING_CODE_MANUAL_PAIRING_CODE, type = "java.lang.String"), + @ActionOutput(name = "qrPairingCode", label = MatterBindingConstants.THING_ACTION_LABEL_NODE_GENERATE_NEW_PAIRING_CODE_QR_PAIRING_CODE, type = "qrCode") }) Map generateNewPairingCode() { + NodeHandler handler = this.handler; + if (handler != null) { + MatterControllerClient client = handler.getClient(); + if (client != null) { + try { + PairingCodes code = client.enhancedCommissioningWindow(handler.getNodeId()).get(); + return Map.of("manualPairingCode", code.manualPairingCode, "qrPairingCode", code.qrPairingCode); + } catch (InterruptedException | ExecutionException | JsonParseException e) { + logger.debug("Failed to generate new pairing code for device {}", handler.getNodeId(), e); + } + } + } + return Map.of("manualPairingCode", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER), + "qrPairingCode", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER)); + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_NODE_DECOMMISSION, description = MatterBindingConstants.THING_ACTION_DESC_NODE_DECOMMISSION) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_NODE_DECOMMISSION_RESULT, type = "java.lang.String") }) String decommissionNode() { + NodeHandler handler = this.handler; + if (handler != null) { + MatterControllerClient client = handler.getClient(); + if (client != null) { + try { + client.removeNode(handler.getNodeId()).get(); + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_SUCCESS); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Failed to decommission device {}", handler.getNodeId(), e); + return Objects.requireNonNull(Optional.ofNullable(e.getLocalizedMessage()).orElse(e.toString())); + } + } + } + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER); + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_NODE_GET_FABRICS, description = MatterBindingConstants.THING_ACTION_DESC_NODE_GET_FABRICS) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_NODE_GET_FABRICS_RESULT, type = "java.lang.String") }) String getFabrics() { + NodeHandler handler = this.handler; + if (handler != null) { + MatterControllerClient client = handler.getClient(); + if (client != null) { + try { + List fabrics = client + .getFabrics(handler.getNodeId()).get(); + String result = fabrics.stream().map(fabric -> String.format("#%d %s (%s)", fabric.fabricIndex, + fabric.label, MatterVendorIDs.VENDOR_IDS.get(fabric.vendorId))) + .collect(Collectors.joining(", ")); + return result.isEmpty() + ? translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_FABRICS) + : result; + } catch (InterruptedException | ExecutionException | JsonParseException e) { + logger.debug("Failed to retrieve fabrics {}", handler.getNodeId(), e); + return Objects.requireNonNull(Optional.ofNullable(e.getLocalizedMessage()).orElse(e.toString())); + } + } + } + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER); + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_NODE_REMOVE_FABRIC, description = MatterBindingConstants.THING_ACTION_DESC_NODE_REMOVE_FABRIC) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_NODE_REMOVE_FABRIC_RESULT, type = "java.lang.String") }) String removeFabric( + @ActionInput(name = "indexNumber", label = MatterBindingConstants.THING_ACTION_LABEL_NODE_REMOVE_FABRIC_INDEX, description = MatterBindingConstants.THING_ACTION_DESC_NODE_REMOVE_FABRIC_INDEX) Integer indexNumber) { + NodeHandler handler = this.handler; + if (handler != null) { + MatterControllerClient client = handler.getClient(); + if (client != null) { + try { + client.removeFabric(handler.getNodeId(), indexNumber).get(); + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_SUCCESS); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Failed to remove fabric {} {} ", handler.getNodeId(), indexNumber, e); + return Objects.requireNonNull(Optional.ofNullable(e.getLocalizedMessage()).orElse(e.toString())); + } + } + } + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterOTBRActions.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterOTBRActions.java new file mode 100644 index 00000000000..fc711a6d186 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/actions/MatterOTBRActions.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.actions; + +import java.security.NoSuchAlgorithmException; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterBindingConstants; +import org.openhab.binding.matter.internal.controller.devices.converter.ThreadBorderRouterManagementConverter; +import org.openhab.binding.matter.internal.handler.NodeHandler; +import org.openhab.binding.matter.internal.util.ThreadDataset; +import org.openhab.binding.matter.internal.util.ThreadDataset.ThreadTimestamp; +import org.openhab.binding.matter.internal.util.TlvCodec; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.automation.annotation.ActionInput; +import org.openhab.core.automation.annotation.ActionOutput; +import org.openhab.core.automation.annotation.ActionOutputs; +import org.openhab.core.automation.annotation.RuleAction; +import org.openhab.core.thing.binding.ThingActions; +import org.openhab.core.thing.binding.ThingActionsScope; +import org.openhab.core.thing.binding.ThingHandler; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link MatterOTBRActions} exposes Thread Border Router related actions + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(scope = ServiceScope.PROTOTYPE, service = MatterOTBRActions.class) +@ThingActionsScope(name = "matter-otbr") +public class MatterOTBRActions implements ThingActions { + public final Logger logger = LoggerFactory.getLogger(getClass()); + + protected @Nullable NodeHandler handler; + private final TranslationService translationService; + + @Activate + public MatterOTBRActions(@Reference TranslationService translationService) { + this.translationService = translationService; + } + + @Override + public void setThingHandler(@Nullable ThingHandler handler) { + if (handler instanceof NodeHandler nodeHandler) { + this.handler = nodeHandler; + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return handler; + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_LOAD_EXTERNAL_DATASET, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_LOAD_EXTERNAL_DATASET) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_LOAD_EXTERNAL_DATASET_RESULT, type = "java.lang.String") }) String loadExternalOperationalDataset( + @ActionInput(name = "dataset", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_LOAD_EXTERNAL_DATASET_DATASET, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_LOAD_EXTERNAL_DATASET_DATASET) String dataset) { + NodeHandler handler = this.handler; + if (handler != null) { + ThreadBorderRouterManagementConverter converter = handler + .findConverterByType(ThreadBorderRouterManagementConverter.class); + if (converter != null) { + try { + ThreadDataset tds = null; + if (dataset.trim().startsWith("{")) { + tds = ThreadDataset.fromJson(dataset); + if (tds == null) { + return translationService + .getTranslation(MatterBindingConstants.THING_ACTION_RESULT_INVALID_JSON); + } + } else { + tds = ThreadDataset.fromHex(dataset); + } + converter.updateThreadConfiguration(tds.toHex()); + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_SUCCESS); + } catch (Exception e) { + logger.debug("Error setting dataset", e); + return "error: " + e.getMessage(); + } + } else { + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_CONVERTER); + } + } else { + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER); + } + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_LOAD_ACTIVE_DATASET, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_LOAD_ACTIVE_DATASET) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_LOAD_ACTIVE_DATASET_RESULT, type = "java.lang.String"), + @ActionOutput(name = "dataset", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_LOAD_ACTIVE_DATASET_DATASET, type = "java.lang.String") }) Map loadActiveOperationalDataset() { + NodeHandler handler = this.handler; + if (handler != null) { + ThreadBorderRouterManagementConverter converter = handler + .findConverterByType(ThreadBorderRouterManagementConverter.class); + if (converter != null) { + try { + String dataset = Objects.requireNonNull(converter.getActiveDataset().get(), + "Could not get active dataset"); + converter.updateThreadConfiguration(dataset); + return Map.of("result", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_SUCCESS), + "dataset", dataset); + } catch (Exception e) { + logger.debug("Error setting dataset", e); + String message = Objects.requireNonNull(Optional.ofNullable(e.getMessage()).orElse(e.toString())); + return Map.of("error", message); + } + } else { + return Map.of("error", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_CONVERTER)); + } + } else { + return Map.of("error", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER)); + } + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_PUSH_DATASET, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_PUSH_DATASET) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_PUSH_DATASET_RESULT, type = "java.lang.String") }) String pushOperationalDataSetHex( + @ActionInput(name = "delay", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_PUSH_DATASET_DELAY, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_PUSH_DATASET_DELAY, defaultValue = "30000", required = true) @Nullable Long delay, + @ActionInput(name = "generatePendingTime", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_PUSH_DATASET_GENERATE_TIME, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_PUSH_DATASET_GENERATE_TIME, defaultValue = "true", required = true) @Nullable Boolean generatePendingTime, + @ActionInput(name = "incrementActiveTime", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_PUSH_DATASET_INCREMENT_TIME, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_PUSH_DATASET_INCREMENT_TIME, defaultValue = "1", required = true) @Nullable Integer incrementActiveTime) { + NodeHandler handler = this.handler; + if (handler == null) { + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER); + } + ThreadBorderRouterManagementConverter converter = handler + .findConverterByType(ThreadBorderRouterManagementConverter.class); + if (converter == null) { + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_CONVERTER); + } + ThreadDataset tds = converter.datasetFromConfiguration(); + if (delay == null) { + delay = 30000L; + } + // default to generating a new pending timestamp + if (generatePendingTime == null || generatePendingTime.booleanValue()) { + tds.setPendingTimestamp(ThreadTimestamp.now(false)); + } + ThreadTimestamp ts = Objects + .requireNonNull(tds.getActiveTimestampObject().orElse(new ThreadTimestamp(1, 0, false))); + + ts.setSeconds(ts.getSeconds() + (incrementActiveTime == null ? 1 : incrementActiveTime.intValue())); + tds.setActiveTimestamp(ts); + tds.setDelayTimer(delay); + logger.debug("New dataset: {}", tds.toJson()); + String dataset = tds.toHex(); + logger.debug("New dataset hex: {}", dataset); + try { + converter.setPendingDataset(dataset).get(); + return translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_SUCCESS) + ": " + + tds.toJson(); + } catch (Exception e) { + logger.debug("Error setting pending dataset", e); + return "error: " + e.getMessage(); + } + } + + @RuleAction(label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET) + public @ActionOutputs({ + @ActionOutput(name = "result", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_RESULT, type = "java.lang.String"), + @ActionOutput(name = "datasetJson", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_JSON, type = "java.lang.String"), + @ActionOutput(name = "datasetHex", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_HEX, type = "java.lang.String") }) Map generateOperationalDataset( + @ActionInput(name = "save", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_SAVE, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_SAVE, defaultValue = "false", required = true) @Nullable Boolean save, + @ActionInput(name = "channel", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_CHANNEL, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_CHANNEL, defaultValue = "22", required = true) @Nullable Integer channel, + @ActionInput(name = "activeTimestampSeconds", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TIMESTAMP_SECONDS, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_TIMESTAMP_SECONDS, defaultValue = "1", required = true) @Nullable Long activeTimestampSeconds, + @ActionInput(name = "activeTimestampTicks", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TIMESTAMP_TICKS, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_TIMESTAMP_TICKS, defaultValue = "0", required = true) @Nullable Integer activeTimestampTicks, + @ActionInput(name = "activeTimestampAuthoritative", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TIMESTAMP_AUTHORITATIVE, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_TIMESTAMP_AUTHORITATIVE, defaultValue = "false", required = true) @Nullable Boolean activeTimestampAuthoritative, + @ActionInput(name = "panId", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_PAN_ID, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_PAN_ID, defaultValue = "4460", required = true) @Nullable Integer panId, + @ActionInput(name = "extendedPanId", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_EXTENDED_PAN_ID, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_EXTENDED_PAN_ID, defaultValue = "1111111122222222", required = true) @Nullable String extendedPanId, + @ActionInput(name = "meshLocalPrefix", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_MESH_PREFIX, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_MESH_PREFIX, defaultValue = "fd11:22::/64", required = true) @Nullable String meshLocalPrefix, + @ActionInput(name = "networkName", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NETWORK_NAME, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_NETWORK_NAME, defaultValue = "openHAB-Thread", required = true) @Nullable String networkName, + @ActionInput(name = "networkKey", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NETWORK_KEY, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_NETWORK_KEY, required = false) @Nullable String networkKey, + @ActionInput(name = "passPhrase", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_PASSPHRASE, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_PASSPHRASE, defaultValue = "j01Nme", required = true) @Nullable String passPhrase, + @ActionInput(name = "rotationTime", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_ROTATION_TIME, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_ROTATION_TIME, defaultValue = "672") @Nullable Integer rotationTime, + @ActionInput(name = "obtainNetworkKey", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_OBTAIN_NETWORK_KEY, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_OBTAIN_NETWORK_KEY, defaultValue = "true") @Nullable Boolean obtainNetworkKey, + @ActionInput(name = "nativeCommissioning", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NATIVE_COMMISSIONING, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_NATIVE_COMMISSIONING, defaultValue = "true") @Nullable Boolean nativeCommissioning, + @ActionInput(name = "routers", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_ROUTERS, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_ROUTERS, defaultValue = "true") @Nullable Boolean routers, + @ActionInput(name = "externalCommissioning", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_EXTERNAL_COMMISSIONING, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_EXTERNAL_COMMISSIONING, defaultValue = "true") @Nullable Boolean externalCommissioning, + @ActionInput(name = "commercialCommissioning", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_COMMERCIAL_COMMISSIONING, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_COMMERCIAL_COMMISSIONING, defaultValue = "false") @Nullable Boolean commercialCommissioning, + @ActionInput(name = "autonomousEnrollment", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_AUTONOMOUS_ENROLLMENT, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_AUTONOMOUS_ENROLLMENT, defaultValue = "true") @Nullable Boolean autonomousEnrollment, + @ActionInput(name = "networkKeyProvisioning", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NETWORK_KEY_PROVISIONING, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_NETWORK_KEY_PROVISIONING, defaultValue = "true") @Nullable Boolean networkKeyProvisioning, + @ActionInput(name = "tobleLink", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_TOBLE_LINK, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_TOBLE_LINK, defaultValue = "true") @Nullable Boolean tobleLink, + @ActionInput(name = "nonCcmRouters", label = MatterBindingConstants.THING_ACTION_LABEL_OTBR_GENERATE_DATASET_NON_CCM_ROUTERS, description = MatterBindingConstants.THING_ACTION_DESC_OTBR_GENERATE_DATASET_NON_CCM_ROUTERS, defaultValue = "false") @Nullable Boolean nonCcmRouters) { + NodeHandler handler = this.handler; + if (handler == null) { + return Map.of("error", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_HANDLER)); + } + ThreadBorderRouterManagementConverter converter = handler + .findConverterByType(ThreadBorderRouterManagementConverter.class); + if (converter == null) { + return Map.of("error", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_NO_CONVERTER)); + } + try { + ThreadTimestamp timestamp = new ThreadTimestamp(1, 0, false); + if (activeTimestampSeconds != null) { + timestamp.setSeconds(activeTimestampSeconds.longValue()); + } + if (activeTimestampTicks != null) { + timestamp.setTicks(activeTimestampTicks.intValue()); + } + if (activeTimestampAuthoritative != null) { + timestamp.setAuthoritative(activeTimestampAuthoritative.booleanValue()); + } + long channelMask = 134215680; + if (channel == null) { + channel = 22; + } + if (panId == null) { + panId = 4460; + } + if (extendedPanId == null) { + extendedPanId = "1111111122222222"; + } + if (meshLocalPrefix == null) { + meshLocalPrefix = "fd11:22::/64"; + } + if (networkName == null) { + networkName = "openHAB-Thread"; + } + if (passPhrase == null) { + passPhrase = "j01Nme"; + } + if (networkKey == null) { + try { + networkKey = TlvCodec.bytesToHex(ThreadDataset.generateMasterKey()); + } catch (NoSuchAlgorithmException e) { + logger.debug("Error generating master key", e); + return Map.of("error", + translationService + .getTranslation(MatterBindingConstants.THING_ACTION_RESULT_ERROR_GENERATING_KEY) + + ": " + e.getMessage()); + } + } + if (save == null) { + save = false; + } + + String pskc = TlvCodec.bytesToHex(ThreadDataset.generatePskc(passPhrase, networkName, extendedPanId)); + + logger.debug( + "All values: channel: {}, panId: {}, extendedPanId: {}, meshLocalPrefix: {}, networkName: {}, networkKey: {}, pskc: {}", + channel, panId, extendedPanId, meshLocalPrefix, networkName, networkKey, pskc); + + ThreadDataset dataset = new ThreadDataset(timestamp, null, null, channel, channelMask, panId, networkName, + networkKey, extendedPanId, pskc, meshLocalPrefix, null); + + int rotationTimeValue = (rotationTime == null) ? 672 : rotationTime.intValue(); + dataset.setSecurityPolicyRotation(rotationTimeValue); + dataset.setObtainNetworkKey(obtainNetworkKey != null ? obtainNetworkKey.booleanValue() : true); + dataset.setNativeCommissioning(nativeCommissioning != null ? nativeCommissioning.booleanValue() : true); + dataset.setRoutersEnabled(routers != null ? routers.booleanValue() : true); + dataset.setCommercialCommissioning( + commercialCommissioning != null ? commercialCommissioning.booleanValue() : false); + dataset.setExternalCommissioning( + externalCommissioning != null ? externalCommissioning.booleanValue() : true); + dataset.setAutonomousEnrollment(autonomousEnrollment != null ? autonomousEnrollment.booleanValue() : true); + dataset.setNetworkKeyProvisioning( + networkKeyProvisioning != null ? networkKeyProvisioning.booleanValue() : true); + dataset.setToBleLink(tobleLink != null ? tobleLink.booleanValue() : true); + dataset.setNonCcmRouters(nonCcmRouters != null ? nonCcmRouters.booleanValue() : false); + + String json = dataset.toJson(); + String hex = dataset.toHex(); + logger.debug("Generated dataset: {}", json); + logger.debug("Generated dataset hex: {}", hex); + if (save.booleanValue()) { + converter.updateThreadConfiguration(hex); + } + return Map.of("result", + translationService.getTranslation(MatterBindingConstants.THING_ACTION_RESULT_SUCCESS), + "datasetJson", json, "datasetHex", hex); + } catch (Exception e) { + logger.debug("Error setting active dataset", e); + return Map.of("error", translationService + .getTranslation(MatterBindingConstants.THING_ACTION_RESULT_ERROR_SETTING_DATASET)); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridge.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridge.java new file mode 100644 index 00000000000..eb957ebb533 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridge.java @@ -0,0 +1,577 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.devices.DeviceRegistry; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice; +import org.openhab.binding.matter.internal.client.MatterClientListener; +import org.openhab.binding.matter.internal.client.MatterWebsocketService; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeCommissionState; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventAttributeChanged; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventMessage; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventTriggered; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeDataMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeStateMessage; +import org.openhab.core.OpenHAB; +import org.openhab.core.common.ThreadPoolManager; +import org.openhab.core.common.registry.RegistryChangeListener; +import org.openhab.core.config.core.ConfigurableService; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.ItemNotFoundException; +import org.openhab.core.items.ItemRegistry; +import org.openhab.core.items.ItemRegistryChangeListener; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.osgi.framework.Constants; +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonParseException; + +/** + * The {@link MatterBridge} is the main class for the Matter Bridge service. + * + * It is responsible for exposing a "Matter Bridge" server and exposing items as endpoint on the bridge. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@Component(immediate = true, service = MatterBridge.class, configurationPid = MatterBridge.CONFIG_PID, property = Constants.SERVICE_PID + + "=" + MatterBridge.CONFIG_PID) +@ConfigurableService(category = "io", label = "Matter Bridge", description_uri = MatterBridge.CONFIG_URI) +public class MatterBridge implements MatterClientListener { + private final Logger logger = LoggerFactory.getLogger(MatterBridge.class); + private static final String CONFIG_PID = "org.openhab.matter"; + private static final String CONFIG_URI = "io:matter"; + + // Matter Bridge Device Info *Basic Information Cluster* + private static final String VENDOR_NAME = "openHAB"; + private static final String DEVICE_NAME = "Bridge Device"; + private static final String PRODUCT_ID = "0001"; + private static final String VENDOR_ID = "65521"; + + private final Map devices = new HashMap<>(); + + private MatterBridgeClient client; + private ItemRegistry itemRegistry; + private MetadataRegistry metadataRegistry; + private MatterWebsocketService websocketService; + private ConfigurationAdmin configAdmin; + private MatterBridgeSettings settings; + + private final ItemRegistryChangeListener itemRegistryChangeListener; + private final RegistryChangeListener metadataRegistryChangeListener; + private final ScheduledExecutorService scheduler = ThreadPoolManager + .getScheduledPool(ThreadPoolManager.THREAD_POOL_NAME_COMMON); + private boolean resetStorage = false; + private @Nullable ScheduledFuture modifyFuture; + private @Nullable ScheduledFuture reconnectFuture; + private RunningState runningState = RunningState.Stopped; + + @Activate + public MatterBridge(final @Reference ItemRegistry itemRegistry, final @Reference MetadataRegistry metadataRegistry, + final @Reference MatterWebsocketService websocketService, final @Reference ConfigurationAdmin configAdmin) { + this.itemRegistry = itemRegistry; + this.metadataRegistry = metadataRegistry; + this.websocketService = websocketService; + this.configAdmin = configAdmin; + this.client = new MatterBridgeClient(); + this.settings = new MatterBridgeSettings(); + + itemRegistryChangeListener = new ItemRegistryChangeListener() { + private boolean handleMetadataChange(Item item) { + if (metadataRegistry.get(new MetadataKey("matter", item.getUID())) != null) { + updateModifyFuture(); + return true; + } + return false; + } + + @Override + public void added(Item element) { + handleMetadataChange(element); + } + + @Override + public void updated(Item oldElement, Item element) { + if (!handleMetadataChange(oldElement)) { + handleMetadataChange(element); + } + } + + @Override + public void allItemsChanged(Collection oldItemNames) { + updateModifyFuture(); + } + + @Override + public void removed(Item element) { + handleMetadataChange(element); + } + }; + this.itemRegistry.addRegistryChangeListener(itemRegistryChangeListener); + + metadataRegistryChangeListener = new RegistryChangeListener<>() { + private boolean handleMetadataChange(Metadata element) { + if ("matter".equals(element.getUID().getNamespace())) { + updateModifyFuture(); + return true; + } + return false; + } + + public void added(Metadata element) { + handleMetadataChange(element); + } + + public void removed(Metadata element) { + handleMetadataChange(element); + } + + public void updated(Metadata oldElement, Metadata element) { + if (!handleMetadataChange(oldElement)) { + handleMetadataChange(element); + } + } + }; + this.metadataRegistry.addRegistryChangeListener(metadataRegistryChangeListener); + } + + @Activate + public void activate(Map properties) { + logger.debug("Activating Matter Bridge {}", properties); + // if this returns true, we will wait for @Modified to be called after the config is persisted + if (!parseInitialConfig(properties)) { + this.settings = (new Configuration(properties)).as(MatterBridgeSettings.class); + if (this.settings.enableBridge) { + connectClient(); + } + } + } + + @Deactivate + public void deactivate() { + logger.debug("Deactivating Matter Bridge"); + itemRegistry.removeRegistryChangeListener(itemRegistryChangeListener); + metadataRegistry.removeRegistryChangeListener(metadataRegistryChangeListener); + stopClient(); + } + + @Modified + protected void modified(Map properties) { + logger.debug("Modified Matter Bridge {}", properties); + MatterBridgeSettings settings = (new Configuration(properties)).as(MatterBridgeSettings.class); + boolean restart = false; + if (this.settings.enableBridge != settings.enableBridge) { + restart = true; + } + if (!this.settings.bridgeName.equals(settings.bridgeName)) { + restart = true; + } + if (this.settings.discriminator != settings.discriminator) { + restart = true; + } + if (this.settings.passcode != settings.passcode) { + restart = true; + } + if (this.settings.port != settings.port) { + restart = true; + } + if (settings.resetBridge) { + this.resetStorage = true; + settings.resetBridge = false; + restart = true; + } + + this.settings = settings; + + if (!settings.enableBridge) { + stopClient(); + } else if (!client.isConnected() || restart) { + stopClient(); + scheduleConnect(); + } else { + manageCommissioningWindow(settings.openCommissioningWindow); + } + } + + @Override + public void onDisconnect(String reason) { + stopClient(); + if (this.settings.enableBridge) { + scheduleConnect(); + } + } + + @Override + public void onConnect() { + } + + @Override + public void onReady() { + registerItems(); + } + + @Override + public void onEvent(NodeStateMessage message) { + } + + @Override + public void onEvent(AttributeChangedMessage message) { + } + + @Override + public void onEvent(EventTriggeredMessage message) { + } + + @Override + public void onEvent(NodeDataMessage message) { + } + + @Override + public void onEvent(BridgeEventMessage message) { + if (message instanceof BridgeEventAttributeChanged attributeChanged) { + GenericDevice d = devices.get(attributeChanged.data.endpointId); + if (d != null) { + d.handleMatterEvent(attributeChanged.data.clusterName, attributeChanged.data.attributeName, + attributeChanged.data.data); + } + } else if (message instanceof BridgeEventTriggered bridgeEventTriggered) { + switch (bridgeEventTriggered.data.eventName) { + case "commissioningWindowOpen": + updateConfig(Map.of("openCommissioningWindow", true)); + break; + case "commissioningWindowClosed": + updateConfig(Map.of("openCommissioningWindow", false)); + break; + default: + } + } + } + + public void restart() { + stopClient(); + connectClient(); + } + + public void allowCommissioning() { + manageCommissioningWindow(true); + } + + public void resetStorage() { + this.resetStorage = true; + stopClient(); + connectClient(); + } + + public String listFabrics() throws InterruptedException, ExecutionException { + return client.getFabrics().get().toString(); + } + + public void removeFabric(String fabricId) { + try { + client.removeFabric(Integer.parseInt(fabricId)).get(); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Could not remove fabric", e); + } + } + + private synchronized void connectClient() { + if (client.isConnected()) { + logger.debug("Already Connected, returning"); + return; + } + + String folderName = OpenHAB.getUserDataFolder() + File.separator + "matter"; + File folder = new File(folderName); + if (!folder.exists()) { + folder.mkdirs(); + } + + Map paramsMap = new HashMap<>(); + + paramsMap.put("service", "bridge"); + paramsMap.put("storagePath", folder.getAbsolutePath()); + + // default values the bridge exposes to clients + paramsMap.put("deviceName", DEVICE_NAME); + paramsMap.put("vendorName", VENDOR_NAME); + paramsMap.put("vendorId", VENDOR_ID); + paramsMap.put("productId", PRODUCT_ID); + + paramsMap.put("productName", settings.bridgeName); + paramsMap.put("passcode", String.valueOf(settings.passcode)); + paramsMap.put("discriminator", String.valueOf(settings.discriminator)); + paramsMap.put("port", String.valueOf(settings.port)); + + client.addListener(this); + client.connectWhenReady(this.websocketService, paramsMap); + } + + private void stopClient() { + logger.debug("Stopping Matter Bridge Client"); + cancelConnect(); + updateRunningState(RunningState.Stopped, null); + ScheduledFuture modifyFuture = this.modifyFuture; + if (modifyFuture != null) { + modifyFuture.cancel(true); + } + client.removeListener(this); + client.disconnect(); + devices.values().forEach(GenericDevice::dispose); + devices.clear(); + } + + private void scheduleConnect() { + cancelConnect(); + this.reconnectFuture = scheduler.schedule(this::connectClient, 5, TimeUnit.SECONDS); + } + + private void cancelConnect() { + ScheduledFuture reconnectFuture = this.reconnectFuture; + if (reconnectFuture != null) { + reconnectFuture.cancel(true); + } + } + + private boolean parseInitialConfig(Map properties) { + logger.debug("Parse Config Matter Bridge"); + + Dictionary props = null; + org.osgi.service.cm.Configuration config = null; + + try { + config = configAdmin.getConfiguration(MatterBridge.CONFIG_PID); + props = config.getProperties(); + } catch (IOException e) { + logger.warn("cannot retrieve config admin {}", e.getMessage()); + } + + if (props == null) { // if null, the configuration is new + props = new Hashtable<>(); + } + + // A discriminator uniquely identifies a Matter device on the IPV6 network, 12-bit integer (0-4095) + int discriminator = -1; + @Nullable + Object discriminatorProp = props.get("discriminator"); + if (discriminatorProp instanceof String discriminatorString) { + try { + discriminator = Integer.parseInt(discriminatorString); + } catch (NumberFormatException e) { + logger.debug("Could not parse discriminator {}", discriminatorString); + } + } else if (discriminatorProp instanceof Integer discriminatorInteger) { + discriminator = discriminatorInteger; + } + + // randomly create one if not set + if (discriminator < 0) { + Random random = new Random(); + discriminator = random.nextInt(4096); + } + + props.put("discriminator", discriminator); + + // this should never be persisted true, temporary settings + props.put("resetBridge", false); + + boolean changed = false; + if (config != null) { + try { + changed = config.updateIfDifferent(props); + } catch (IOException e) { + logger.warn("cannot update configuration {}", e.getMessage()); + } + } + return changed; + } + + private synchronized void registerItems() { + try { + logger.debug("Initializing bridge, resetStorage: {}", resetStorage); + client.initializeBridge(resetStorage).get(); + if (resetStorage) { + resetStorage = false; + updateConfig(Map.of("resetBridge", false)); + } + } catch (InterruptedException | ExecutionException e) { + logger.debug("Could not initialize endpoints", e); + updateRunningState(RunningState.Error, e.getMessage()); + return; + } + + updateRunningState(RunningState.Starting, null); + + // clear out any existing devices + devices.values().forEach(GenericDevice::dispose); + devices.clear(); + + for (Metadata metadata : metadataRegistry.getAll()) { + final MetadataKey uid = metadata.getUID(); + if ("matter".equals(uid.getNamespace())) { + try { + logger.debug("Metadata {}", metadata); + if (devices.containsKey(uid.getItemName())) { + logger.debug("Updating item {}", uid.getItemName()); + } + final GenericItem item = (GenericItem) itemRegistry.getItem(uid.getItemName()); + String deviceType = metadata.getValue(); + String[] parts = deviceType.split(","); + for (String part : parts) { + GenericDevice device = DeviceRegistry.createDevice(part.trim(), metadataRegistry, client, item); + if (device != null) { + try { + device.registerDevice().get(); + logger.debug("Registered item {} with device type {}", item.getName(), + device.deviceType()); + devices.put(item.getName(), device); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Could not register device with bridge", e); + updateRunningState(RunningState.Error, e.getMessage()); + device.dispose(); + devices.values().forEach(GenericDevice::dispose); + devices.clear(); + return; + } + break; + } + } + } catch (ItemNotFoundException e) { + logger.debug("Could not find item {}", uid.getItemName(), e); + } + } + } + if (devices.isEmpty()) { + logger.info("No devices found to register with bridge, not starting bridge"); + updateRunningState(RunningState.Stopped, "No items found with matter metadata"); + return; + } + try { + client.startBridge().get(); + updateRunningState(RunningState.Running, null); + updatePairingCodes(); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Could not start bridge", e); + } + } + + private void manageCommissioningWindow(boolean open) { + if (runningState != RunningState.Running) { + return; + } + if (open) { + try { + client.openCommissioningWindow().get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + logger.debug("Could not open commissioning window", e); + } + } else { + try { + client.closeCommissioningWindow().get(); + } catch (CancellationException | InterruptedException | ExecutionException e) { + logger.debug("Could not close commissioning window", e); + } + } + } + + private void updatePairingCodes() { + try { + BridgeCommissionState state = client.getCommissioningState().get(); + updateConfig(Map.of("manualPairingCode", state.pairingCodes.manualPairingCode, "qrCode", + state.pairingCodes.qrPairingCode, "openCommissioningWindow", state.commissioningWindowOpen)); + } catch (CancellationException | InterruptedException | ExecutionException | JsonParseException e) { + logger.debug("Could not query codes", e); + } + } + + private void updateConfig(Map entries) { + try { + org.osgi.service.cm.Configuration config = configAdmin.getConfiguration(MatterBridge.CONFIG_PID); + Dictionary props = config.getProperties(); + if (props == null) { + return; + } + entries.forEach((k, v) -> props.put(k, v)); + // if this updates, it will trigger a @Modified call + config.updateIfDifferent(props); + } catch (IOException e) { + logger.debug("Could not load configuration", e); + } + } + + private void updateRunningState(RunningState newState, @Nullable String message) { + runningState = newState; + updateConfig(Map.of("runningState", runningState.toString() + (message != null ? ": " + message : ""))); + } + + /** + * This should be called by changes to items or metadata + */ + private void updateModifyFuture() { + // if the bridge is not enabled, we don't need to update the future + if (!settings.enableBridge) { + return; + } + ScheduledFuture modifyFuture = this.modifyFuture; + if (modifyFuture != null) { + modifyFuture.cancel(true); + } + this.modifyFuture = scheduler.schedule(this::registerItems, 5, TimeUnit.SECONDS); + } + + enum RunningState { + Stopped("Stopped"), + Starting("Starting"), + Running("Running"), + Error("Error"); + + private final String runningState; + + RunningState(String runningState) { + this.runningState = runningState; + } + + @Override + public String toString() { + return runningState; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridgeClient.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridgeClient.java new file mode 100644 index 00000000000..c079b44bfa8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridgeClient.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.MatterWebsocketClient; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeCommissionState; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +/** + * The {@link MatterBridgeClient} is a client for the Matter Bridge service. + * + * It is responsible for sending messages to the Matter Bridge websocket server and receiving responses. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterBridgeClient extends MatterWebsocketClient { + + public CompletableFuture addEndpoint(String deviceType, String id, String nodeLabel, String productName, + String productLabel, String serialNumber, Map> attributeMap) { + CompletableFuture future = sendMessage("bridge", "addEndpoint", + new Object[] { deviceType, id, nodeLabel, productName, productLabel, serialNumber, attributeMap }); + return future.thenApply(obj -> obj.toString()); + } + + public CompletableFuture setEndpointState(String endpointId, String clusterName, String attributeName, + Object state) { + CompletableFuture future = sendMessage("bridge", "setEndpointState", + new Object[] { endpointId, clusterName, attributeName, state }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + public CompletableFuture initializeBridge(boolean resetStorage) { + CompletableFuture future = sendMessage("bridge", "initializeBridge", + new Object[] { resetStorage }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + public CompletableFuture startBridge() { + CompletableFuture future = sendMessage("bridge", "startBridge", new Object[0]); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + public CompletableFuture getCommissioningState() { + CompletableFuture future = sendMessage("bridge", "getCommissioningState", new Object[0]); + return future.thenApply(obj -> { + BridgeCommissionState state = gson.fromJson(obj, BridgeCommissionState.class); + if (state == null) { + throw new JsonParseException("Could not deserialize commissioning state"); + } + return state; + }); + } + + public CompletableFuture openCommissioningWindow() { + CompletableFuture future = sendMessage("bridge", "openCommissioningWindow", new Object[0]); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + public CompletableFuture closeCommissioningWindow() { + CompletableFuture future = sendMessage("bridge", "closeCommissioningWindow", new Object[0]); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + public CompletableFuture removeFabric(int fabricIndex) { + CompletableFuture future = sendMessage("bridge", "removeFabric", new Object[] { fabricIndex }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + public CompletableFuture getFabrics() { + CompletableFuture future = sendMessage("bridge", "getFabrics", new Object[0]); + return future.thenApply(obj -> { + return obj.toString(); + }); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridgeSettings.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridgeSettings.java new file mode 100644 index 00000000000..4e3e2a49b10 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/MatterBridgeSettings.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link MatterBridgeSettings} is the settings configuration for the Matter Bridge service. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterBridgeSettings { + public boolean enableBridge = true; + public String runningState = "Stopped"; + public String bridgeName = "openHAB"; + public int port = 5540; + public int passcode = 20202021; + public int discriminator = -1; + public String qrCode = ""; + public String manualPairingCode = ""; + public boolean resetBridge = false; + public boolean openCommissioningWindow = false; + + public String toString() { + return "MatterBridgeSettings [name=" + bridgeName + ", port=" + port + ", passcode=" + passcode + + ", discriminator=" + discriminator + ", qrCode=" + qrCode + ", manualPairingCode=" + manualPairingCode + + ", resetBridge=" + resetBridge + ", openCommissioningWindow=" + openCommissioningWindow + "]"; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ColorDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ColorDevice.java new file mode 100644 index 00000000000..c3408efb5f2 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ColorDevice.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.ColorItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; +import org.openhab.core.util.ColorUtil; + +/** + * The {@link ColorDevice} is a device that represents a Color Light. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ColorDevice extends GenericDevice { + // how long to wait (max) for the device to turn on before updating the HSB values + private static final int ONOFF_DELAY_MILLIS = 500; + // the onFuture is used to wait for the device to turn on before updating the HSB values + private CompletableFuture onFuture = CompletableFuture.completedFuture(null); + // the lastH, lastS are used to store the last HSB values as they come in from the device + private @Nullable DecimalType lastH; + private @Nullable PercentType lastS; + private @Nullable PercentType lastB; + + public ColorDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "ColorLight"; + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + if (primaryItem instanceof ColorItem colorItem) { + HSBType hsbType = colorItem.getStateAs(HSBType.class); + if (hsbType == null) { + hsbType = new HSBType(); + } + Integer currentHue = toHue(hsbType.getHue()); + Integer currentSaturation = toSaturation(hsbType.getSaturation()); + Integer currentLevel = toBrightness(hsbType.getBrightness()); + attributeMap.put(LevelControlCluster.CLUSTER_PREFIX + "." + LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL, + Math.max(currentLevel, 1)); + attributeMap.put(ColorControlCluster.CLUSTER_PREFIX + "." + ColorControlCluster.ATTRIBUTE_CURRENT_HUE, + currentHue); + attributeMap.put( + ColorControlCluster.CLUSTER_PREFIX + "." + ColorControlCluster.ATTRIBUTE_CURRENT_SATURATION, + currentSaturation); + attributeMap.put(OnOffCluster.CLUSTER_PREFIX + "." + OnOffCluster.ATTRIBUTE_ON_OFF, currentLevel > 0); + } + + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + Double value = Double.valueOf(0); + if (data instanceof Double d) { + value = d; + } + switch (attributeName) { + case OnOffCluster.ATTRIBUTE_ON_OFF: + updateOnOff(Boolean.valueOf(data.toString())); + break; + case LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL: + updateBrightness(ValueUtils.levelToPercent(value.intValue())); + break; + // currentHue and currentSaturation will always be updated together sequentially in the matter.js bridge + // code + case ColorControlCluster.ATTRIBUTE_CURRENT_HUE: + float hueValue = value == 0 ? 0.0f : value.floatValue() * 360.0f / 254.0f; + lastH = new DecimalType(Float.valueOf(hueValue).toString()); + updateHueSaturation(); + break; + case ColorControlCluster.ATTRIBUTE_CURRENT_SATURATION: + float saturationValue = value == 0 ? 0.0f : value.floatValue() / 254.0f * 100.0f; + lastS = new PercentType(Float.valueOf(saturationValue).toString()); + updateHueSaturation(); + break; + case ColorControlCluster.ATTRIBUTE_COLOR_TEMPERATURE_MIREDS: + Double kelvin = 1e6 / (Double) data; + HSBType ctHSB = ColorUtil.xyToHsb(ColorUtil.kelvinToXY(Math.max(1000, Math.min(kelvin, 10000)))); + lastH = ctHSB.getHue(); + lastS = ctHSB.getSaturation(); + updateHueSaturation(); + break; + default: + break; + } + } + + @Override + public void updateState(Item item, State state) { + if (state instanceof HSBType hsb) { + if (hsb.getBrightness().intValue() == 0) { + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, false); + } else { + // since we are on, complete the future + completeOnFuture(); + lastB = null; // reset the cached brightness + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, true); + setEndpointState(LevelControlCluster.CLUSTER_PREFIX, LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL, + toBrightness(hsb.getBrightness())); + } + setEndpointState(ColorControlCluster.CLUSTER_PREFIX, ColorControlCluster.ATTRIBUTE_CURRENT_HUE, + toHue(hsb.getHue())); + setEndpointState(ColorControlCluster.CLUSTER_PREFIX, ColorControlCluster.ATTRIBUTE_CURRENT_SATURATION, + toSaturation(hsb.getSaturation())); + } + } + + private void updateBrightness(PercentType brightness) { + if (primaryItem instanceof ColorItem colorItem) { + lastB = brightness; + colorItem.send(brightness); + } + } + + private synchronized void updateOnOff(boolean onOff) { + if (primaryItem instanceof ColorItem colorItem) { + if (!onFuture.isDone()) { + onFuture.cancel(true); + } + // if we are turning on, we need to wait for the device to turn on before updating the HSB due to brightness + // being 0 until the device has turned on (and we need to query this state) + if (onOff) { + onFuture = new CompletableFuture<>(); + onFuture.orTimeout(ONOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS); + onFuture.whenComplete((v, ex) -> { + if (lastH != null && lastS != null) { + // if these are not null, we need to update the HSB now + updateHSB(); + } + }); + } + colorItem.send(OnOffType.from(onOff)); + } + } + + private synchronized void updateHSB() { + if (primaryItem instanceof ColorItem colorItem) { + HSBType hsb = colorItem.getStateAs(HSBType.class); + if (hsb == null) { + return; + } + + DecimalType lastH = this.lastH; + PercentType lastS = this.lastS; + PercentType lastB = this.lastB; + + if (lastH == null && lastS == null) { + return; + } + + DecimalType h = hsb.getHue(); + PercentType s = hsb.getSaturation(); + PercentType b = hsb.getBrightness(); + + if (lastH != null) { + h = lastH; + } + if (lastS != null) { + s = lastS; + } + if (lastB != null) { + b = lastB; + } + // the device is still off but should not be, just set the brightness to 100% + if (b.intValue() == 0) { + b = new PercentType(100); + } + colorItem.send(new HSBType(h, s, b)); + } + this.lastH = null; + this.lastS = null; + } + + private void updateHueSaturation() { + if (onFuture.isDone() && lastH != null && lastS != null) { + // we have OnOff and both Hue and Saturation so update + updateHSB(); + } + } + + private synchronized void completeOnFuture() { + if (!onFuture.isDone()) { + onFuture.complete(Void.TYPE.cast(null)); + } + } + + private Integer toHue(DecimalType h) { + return Math.round(h.floatValue() * 254.0f / 360.0f); + } + + private Integer toSaturation(PercentType s) { + return Math.round(s.floatValue() * 254.0f / 100.0f); + } + + private Integer toBrightness(PercentType b) { + return ValueUtils.percentToLevel(b); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ContactSensorDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ContactSensorDevice.java new file mode 100644 index 00000000000..95d9e92ffd0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ContactSensorDevice.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.types.State; + +/** + * The {@link ContactSensorDevice} is a device that represents a Contact Sensor. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ContactSensorDevice extends GenericDevice { + + public ContactSensorDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "ContactSensor"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + attributeMap.put(BooleanStateCluster.CLUSTER_PREFIX + "." + BooleanStateCluster.ATTRIBUTE_STATE_VALUE, + contactState(primaryItem.getState())); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void updateState(Item item, State state) { + setEndpointState(BooleanStateCluster.CLUSTER_PREFIX, BooleanStateCluster.ATTRIBUTE_STATE_VALUE, + contactState(primaryItem.getState())); + } + + /** + * Matter Device Library Specification R1.3 + * 7.1.4.2. Boolean State Cluster + * True: Closed or contact + * False: Open or no contact + * + * @param state + * @return closed or open + */ + private boolean contactState(State state) { + boolean open = true; + if (state instanceof OnOffType onOffType) { + open = onOffType == OnOffType.ON; + } + if (state instanceof OpenClosedType openClosedType) { + open = openClosedType == OpenClosedType.OPEN; + } + return !open; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DeviceRegistry.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DeviceRegistry.java new file mode 100644 index 00000000000..c30d38db354 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DeviceRegistry.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.MetadataRegistry; + +/** + * The {@link DeviceRegistry} is a registry of device types that are supported by the Matter Bridge service. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class DeviceRegistry { + private static final Map> DEVICE_TYPES = new HashMap<>(); + + static { + registerDevice("OnOffLight", OnOffLightDevice.class); + registerDevice("OnOffPlugInUnit", OnOffPlugInUnitDevice.class); + registerDevice("DimmableLight", DimmableLightDevice.class); + registerDevice("Thermostat", ThermostatDevice.class); + registerDevice("WindowCovering", WindowCoveringDevice.class); + registerDevice("DoorLock", DoorLockDevice.class); + registerDevice("TemperatureSensor", TemperatureSensorDevice.class); + registerDevice("HumiditySensor", HumiditySensorDevice.class); + registerDevice("OccupancySensor", OccupancySensorDevice.class); + registerDevice("ContactSensor", ContactSensorDevice.class); + registerDevice("ColorLight", ColorDevice.class); + registerDevice("Fan", FanDevice.class); + } + + private static void registerDevice(String deviceType, Class device) { + DEVICE_TYPES.put(deviceType, device); + } + + public static @Nullable GenericDevice createDevice(String deviceType, MetadataRegistry metadataRegistry, + MatterBridgeClient client, GenericItem item) { + Class clazz = DEVICE_TYPES.get(deviceType); + if (clazz != null) { + try { + Class[] constructorParameterTypes = new Class[] { MetadataRegistry.class, + MatterBridgeClient.class, GenericItem.class }; + Constructor constructor = clazz.getConstructor(constructorParameterTypes); + return constructor.newInstance(metadataRegistry, client, item); + } catch (Exception e) { + // ignore + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DimmableLightDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DimmableLightDevice.java new file mode 100644 index 00000000000..767f50447dc --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DimmableLightDevice.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.DimmerItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; + +/** + * The {@link DimmableLightDevice} is a device that represents a Dimmable Light. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class DimmableLightDevice extends GenericDevice { + + private State lastOnOffState = OnOffType.OFF; + + public DimmableLightDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "DimmableLight"; + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + PercentType level = Optional.ofNullable(primaryItem.getStateAs(PercentType.class)) + .orElseGet(() -> new PercentType(0)); + lastOnOffState = level.intValue() > 0 ? OnOffType.ON : OnOffType.OFF; + attributeMap.put(LevelControlCluster.CLUSTER_PREFIX + "." + LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL, + Math.max(1, ValueUtils.percentToLevel(level))); + attributeMap.put(OnOffCluster.CLUSTER_PREFIX + "." + OnOffCluster.ATTRIBUTE_ON_OFF, level.intValue() > 0); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + switch (attributeName) { + case OnOffCluster.ATTRIBUTE_ON_OFF: + updateOnOff(OnOffType.from(Boolean.valueOf(data.toString()))); + break; + case LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL: + if (lastOnOffState == OnOffType.ON) { + updateLevel(ValueUtils.levelToPercent(((Double) data).intValue())); + } + break; + default: + break; + } + } + + @Override + public void updateState(Item item, State state) { + if (state instanceof HSBType hsb) { + setEndpointState(LevelControlCluster.CLUSTER_PREFIX, LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL, + ValueUtils.percentToLevel(hsb.getBrightness())); + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, + hsb.getBrightness().intValue() > 0); + lastOnOffState = hsb.getBrightness().intValue() > 0 ? OnOffType.ON : OnOffType.OFF; + } else if (state instanceof PercentType percentType) { + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, percentType.intValue() > 0); + if (percentType.intValue() > 0) { + setEndpointState(LevelControlCluster.CLUSTER_PREFIX, LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL, + ValueUtils.percentToLevel(percentType)); + lastOnOffState = OnOffType.ON; + } else { + lastOnOffState = OnOffType.OFF; + } + } else if (state instanceof OnOffType onOffType) { + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, onOffType == OnOffType.ON); + lastOnOffState = onOffType; + } + } + + private void updateOnOff(OnOffType onOffType) { + lastOnOffState = onOffType; + if (primaryItem instanceof GroupItem groupItem) { + groupItem.send(onOffType); + } else { + ((SwitchItem) primaryItem).send(onOffType); + } + } + + private void updateLevel(PercentType level) { + if (primaryItem instanceof GroupItem groupItem) { + groupItem.send(level); + } else { + ((DimmerItem) primaryItem).send(level); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DoorLockDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DoorLockDevice.java new file mode 100644 index 00000000000..922126c54d4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/DoorLockDevice.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DoorLockCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.types.State; + +/** + * The {@link DoorLockDevice} is a device that represents a Door Lock. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class DoorLockDevice extends GenericDevice { + + public DoorLockDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "DoorLock"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + switch (attributeName) { + case DoorLockCluster.ATTRIBUTE_LOCK_STATE: { + int lockInt = ((Double) data).intValue(); + boolean locked = DoorLockCluster.LockStateEnum.LOCKED.getValue() == lockInt; + if (primaryItem instanceof GroupItem groupItem) { + groupItem.send(OnOffType.from(locked)); + } else { + ((SwitchItem) primaryItem).send(OnOffType.from(locked)); + } + } + default: + break; + } + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + attributeMap.put(DoorLockCluster.CLUSTER_PREFIX + "." + DoorLockCluster.ATTRIBUTE_LOCK_STATE, + Optional.ofNullable(primaryItem.getStateAs(OnOffType.class)) + .orElseGet(() -> OnOffType.OFF) == OnOffType.ON ? DoorLockCluster.LockStateEnum.LOCKED.value + : DoorLockCluster.LockStateEnum.UNLOCKED.value); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void updateState(Item item, State state) { + if (state instanceof OnOffType onOffType) { + setEndpointState(DoorLockCluster.CLUSTER_PREFIX, DoorLockCluster.ATTRIBUTE_LOCK_STATE, + onOffType == OnOffType.ON ? DoorLockCluster.LockStateEnum.LOCKED.value + : DoorLockCluster.LockStateEnum.UNLOCKED.value); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/FanDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/FanDevice.java new file mode 100644 index 00000000000..50b4c991be8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/FanDevice.java @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.FanControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.DimmerItem; +import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.items.StringItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.types.State; + +/** + * The {@link FanDevice} is a device that represents a Fan. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class FanDevice extends GenericDevice { + private final Map itemMap = new HashMap<>(); + private final Map attributeToItemNameMap = new HashMap<>(); + private final FanModeMapper fanModeMapper = new FanModeMapper(); + @Nullable + private Integer lastSpeed; + @Nullable + private Integer lastMode; + @Nullable + private OnOffType lastOnOff; + + public FanDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "Fan"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + GenericItem item = itemForAttribute(clusterName, attributeName); + // if we have an item bound to this attribute, we can just update it, otherwise we need to handle updating other + // items (see else block) + if (item != null) { + switch (attributeName) { + case FanControlCluster.ATTRIBUTE_FAN_MODE: + try { + int mode = ((Double) data).intValue(); + String mappedMode = fanModeMapper.toCustomValue(mode); + if (item instanceof NumberItem numberItem) { + numberItem.send(new DecimalType(mappedMode)); + } else if (item instanceof StringItem stringItem) { + stringItem.send(new StringType(mappedMode)); + } else if (item instanceof SwitchItem switchItem) { + switchItem.send(mode > 0 ? OnOffType.ON : OnOffType.OFF); + } + } catch (FanModeMappingException e) { + logger.debug("Could not convert {} to custom value", data); + } + break; + case FanControlCluster.ATTRIBUTE_PERCENT_SETTING: + int level = ((Double) data).intValue(); + if (item instanceof GroupItem groupItem) { + groupItem.send(new PercentType(level)); + } else if (item instanceof DimmerItem dimmerItem) { + dimmerItem.send(new PercentType(level)); + } + break; + case OnOffCluster.ATTRIBUTE_ON_OFF: + if (item instanceof SwitchItem switchItem) { + OnOffType onOff = OnOffType.from((Boolean) data); + switchItem.send(onOff); + lastOnOff = onOff; + } + break; + default: + break; + } + } else { + // if there is not an item bound to a specific attribute, we need to handle updating other items and fake it + switch (attributeName) { + case FanControlCluster.ATTRIBUTE_PERCENT_SETTING: { + int level = ((Double) data).intValue(); + // try and update the on/off state if set + GenericItem genericItem = itemForAttribute(OnOffCluster.CLUSTER_PREFIX, + OnOffCluster.ATTRIBUTE_ON_OFF); + if (genericItem instanceof SwitchItem switchItem) { + switchItem.send(OnOffType.from(level > 0)); + } + // try and update the fan mode if set + genericItem = itemForAttribute(FanControlCluster.CLUSTER_PREFIX, + FanControlCluster.ATTRIBUTE_FAN_MODE); + try { + String mappedMode = fanModeMapper + .toCustomValue(level > 0 ? FanControlCluster.FanModeEnum.ON.value + : FanControlCluster.FanModeEnum.OFF.value); + if (genericItem instanceof NumberItem numberItem) { + numberItem.send(new DecimalType(mappedMode)); + } else if (genericItem instanceof StringItem stringItem) { + stringItem.send(new StringType(mappedMode)); + } else if (genericItem instanceof SwitchItem switchItem) { + switchItem.send(OnOffType.from(level > 0)); + } + } catch (FanModeMappingException e) { + logger.debug("Could not convert {} to custom value", data); + } + } + break; + case FanControlCluster.ATTRIBUTE_FAN_MODE: { + int mode = ((Double) data).intValue(); + GenericItem genericItem = itemForAttribute(FanControlCluster.CLUSTER_PREFIX, + FanControlCluster.ATTRIBUTE_PERCENT_SETTING); + PercentType level = mode > 0 ? PercentType.HUNDRED : PercentType.ZERO; + if (genericItem instanceof GroupItem groupItem) { + groupItem.send(level); + } else if (genericItem instanceof DimmerItem dimmerItem) { + dimmerItem.send(level); + } + // try and update the on/off state if set + genericItem = itemForAttribute(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF); + if (genericItem instanceof SwitchItem switchItem) { + switchItem.send(OnOffType.from(mode > 0)); + } + } + break; + default: + break; + } + } + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + Map attributeMap = new HashMap<>(); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + attributeMap.putAll(primaryMetadata.getAttributeOptions()); + Set members = new HashSet<>(); + members.add(primaryItem); + if (primaryItem instanceof GroupItem groupItem) { + members.addAll(groupItem.getAllMembers()); + } + for (Item member : members) { + if (member instanceof GenericItem genericMember) { + MetaDataMapping metadata = metaDataMapping(genericMember); + State state = genericMember.getState(); + for (String attribute : metadata.attributes) { + String[] pair = attribute.split("\\."); + if (pair.length != 2) { + logger.debug("Unknown attribute format {}", attribute); + continue; + } + String attributeName = pair[1]; + switch (attributeName) { + case FanControlCluster.ATTRIBUTE_PERCENT_SETTING: + if (state instanceof PercentType percentType) { + int speed = percentType.intValue(); + attributeMap.put(attribute, speed); + lastSpeed = speed; + } else { + attributeMap.put(attribute, 0); + lastSpeed = 0; + } + break; + case FanControlCluster.ATTRIBUTE_FAN_MODE: + int mode = 0; + if (state instanceof DecimalType decimalType) { + mode = decimalType.intValue(); + } + attributeMap.put(attribute, mode); + fanModeMapper.initializeMappings(metadata.config); + lastMode = mode; + break; + case OnOffCluster.ATTRIBUTE_ON_OFF: + if (state instanceof OnOffType onOffType) { + attributeMap.put(attribute, onOffType == OnOffType.ON); + lastOnOff = onOffType; + } else { + attributeMap.put(attribute, false); + lastOnOff = OnOffType.OFF; + } + break; + default: + continue; + } + if (!itemMap.containsKey(genericMember.getUID())) { + itemMap.put(genericMember.getUID(), genericMember); + genericMember.addStateChangeListener(this); + } + attributeMap.putAll(metadata.getAttributeOptions()); + attributeToItemNameMap.put(attribute, genericMember.getUID()); + } + } + } + updateMissingAttributes().forEach((attribute, value) -> { + attributeMap.put(attribute, value); + }); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + attributeToItemNameMap.clear(); + primaryItem.removeStateChangeListener(this); + itemMap.forEach((uid, item) -> { + ((GenericItem) item).removeStateChangeListener(this); + }); + itemMap.clear(); + } + + @Override + public void updateState(Item item, State state) { + attributeToItemNameMap.forEach((attribute, itemUid) -> { + if (itemUid.equals(item.getUID())) { + String[] pair = attribute.split("\\."); + if (pair.length != 2) { + logger.debug("Unknown attribute format {}", attribute); + return; + } + String clusterName = pair[0]; + String attributeName = pair[1]; + switch (attributeName) { + case FanControlCluster.ATTRIBUTE_PERCENT_SETTING: + if (state instanceof PercentType percentType) { + int speed = percentType.intValue(); + setEndpointState(clusterName, attributeName, speed); + setEndpointState(clusterName, FanControlCluster.ATTRIBUTE_PERCENT_CURRENT, speed); + lastSpeed = speed; + } + break; + case FanControlCluster.ATTRIBUTE_FAN_MODE: + if (state instanceof OnOffType onOffType) { + int mode = onOffType == OnOffType.ON ? FanControlCluster.FanModeEnum.ON.value + : FanControlCluster.FanModeEnum.OFF.value; + setEndpointState(clusterName, attributeName, mode); + lastMode = mode; + } else { + try { + int mode = fanModeMapper.fromCustomValue(state.toString()).value; + setEndpointState(clusterName, attributeName, mode); + lastMode = mode; + } catch (FanModeMappingException e) { + logger.debug("Could not convert {} to matter value", state.toString()); + } + } + break; + case OnOffCluster.ATTRIBUTE_ON_OFF: + if (state instanceof OnOffType onOffType) { + setEndpointState(clusterName, attributeName, onOffType == OnOffType.ON); + lastOnOff = onOffType; + } + break; + default: + break; + } + } + }); + sendMissingAttributes(); + } + + /** + * Fan device types mandates mode and speed tp be present, this fakes that if those are missing. + * + * @return + */ + private Map updateMissingAttributes() { + Map attributeMap = new HashMap<>(); + OnOffType onOff = lastOnOff; + Integer mode = lastMode; + Integer speed = lastSpeed; + if (lastSpeed == null) { + if (onOff != null) { + attributeMap.put(FanControlCluster.CLUSTER_PREFIX + "." + FanControlCluster.ATTRIBUTE_PERCENT_CURRENT, + onOff == OnOffType.ON ? 100 : 0); + attributeMap.put(FanControlCluster.CLUSTER_PREFIX + "." + FanControlCluster.ATTRIBUTE_PERCENT_SETTING, + onOff == OnOffType.ON ? 100 : 0); + } else if (mode != null) { + attributeMap.put(FanControlCluster.CLUSTER_PREFIX + "." + FanControlCluster.ATTRIBUTE_PERCENT_CURRENT, + mode == 0 ? 0 : 100); + attributeMap.put(FanControlCluster.CLUSTER_PREFIX + "." + FanControlCluster.ATTRIBUTE_PERCENT_SETTING, + mode == 0 ? 0 : 100); + } + } + if (mode == null) { + if (onOff != null) { + attributeMap.put(FanControlCluster.CLUSTER_PREFIX + "." + FanControlCluster.ATTRIBUTE_FAN_MODE, + onOff == OnOffType.ON ? FanControlCluster.FanModeEnum.ON.value + : FanControlCluster.FanModeEnum.OFF.value); + } else if (speed != null) { + attributeMap.put(FanControlCluster.CLUSTER_PREFIX + "." + FanControlCluster.ATTRIBUTE_FAN_MODE, + speed == 0 ? FanControlCluster.FanModeEnum.OFF.value : FanControlCluster.FanModeEnum.ON.value); + } + } + return attributeMap; + } + + private void sendMissingAttributes() { + updateMissingAttributes().forEach((attribute, value) -> { + String[] pair = attribute.split("\\."); + if (pair.length != 2) { + logger.debug("Unknown attribute format {}", attribute); + return; + } + String clusterName = pair[0]; + String attributeName = pair[1]; + setEndpointState(clusterName, attributeName, value); + }); + } + + private @Nullable GenericItem itemForAttribute(String clusterName, String attributeName) { + String pathName = clusterName + "." + attributeName; + String itemUid = attributeToItemNameMap.get(pathName); + if (itemUid != null) { + return itemMap.get(itemUid); + } + return null; + } + + class FanModeMapper { + private final Map intToCustomMap = new HashMap<>(); + private final Map customToEnumMap = new HashMap<>(); + + public FanModeMapper() { + Map mappings = new HashMap<>(); + FanControlCluster.FanModeEnum[] modes = FanControlCluster.FanModeEnum.values(); + for (FanControlCluster.FanModeEnum mode : modes) { + mappings.put(mode.name(), mode.getValue()); + } + initializeMappings(mappings); + } + + public FanModeMapper(Map mappings) { + initializeMappings(mappings); + } + + private void initializeMappings(Map mappings) { + if (mappings.isEmpty()) { + return; + } + + // don't bother mapping if there's no OFF + if (!mappings.containsKey("OFF")) { + return; + } + + intToCustomMap.clear(); + customToEnumMap.clear(); + for (Map.Entry entry : mappings.entrySet()) { + String customKey = entry.getKey().trim(); + Object valueObj = entry.getValue(); + String customValue = valueObj.toString().trim(); + + try { + FanControlCluster.FanModeEnum mode = FanControlCluster.FanModeEnum.valueOf(customKey); + intToCustomMap.put(mode.value, customValue); + customToEnumMap.put(customValue, mode); + } catch (IllegalArgumentException e) { + // ignore unknown values + } + } + } + + public String toCustomValue(int modeValue) throws FanModeMappingException { + String value = intToCustomMap.get(modeValue); + if (value == null) { + throw new FanModeMappingException("No mapping for mode: " + modeValue); + } + return value; + } + + public FanControlCluster.FanModeEnum fromCustomValue(String customValue) throws FanModeMappingException { + FanControlCluster.FanModeEnum value = customToEnumMap.get(customValue); + if (value == null) { + throw new FanModeMappingException("No mapping for custom value: " + customValue); + } + return value; + } + } + + class FanModeMappingException extends Exception { + private static final long serialVersionUID = 1L; + + public FanModeMappingException(String message) { + super(message); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/GenericDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/GenericDevice.java new file mode 100644 index 00000000000..472a75e9fd6 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/GenericDevice.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.items.StateChangeListener; +import org.openhab.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link GenericDevice} is a base class for all devices that are managed by the bridge. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public abstract class GenericDevice implements StateChangeListener { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + protected final GenericItem primaryItem; + protected @Nullable Metadata primaryItemMetadata; + protected final MatterBridgeClient client; + protected final MetadataRegistry metadataRegistry; + protected boolean activated = false; + + public GenericDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem primaryItem) { + this.metadataRegistry = metadataRegistry; + this.client = client; + this.primaryItem = primaryItem; + this.primaryItemMetadata = metadataRegistry.get(new MetadataKey("matter", primaryItem.getUID())); + } + + public abstract String deviceType(); + + /** + * Activate the device, this will return the device options for the device, inherited classes should override this + * method to return the correct device options as well as set the initial state of the device and register listeners + * + * @return the device options + */ + protected abstract MatterDeviceOptions activate(); + + /** + * Dispose of the device, inherited classes should unregister the device and remove the listeners + */ + public abstract void dispose(); + + /** + * Handle openHAB item state changes + * + */ + public abstract void updateState(Item item, State state); + + /** + * Handle matter events + * + * @param clusterName the cluster name + * @param attributeName the attribute name + * @param data the raw matter data value + */ + public abstract void handleMatterEvent(String clusterName, String attributeName, Object data); + + @Override + public void stateChanged(Item item, State oldState, State newState) { + logger.debug("{} state changed from {} to {}", item.getName(), oldState, newState); + updateState(item, newState); + } + + @Override + public void stateUpdated(Item item, State state) { + } + + public synchronized CompletableFuture registerDevice() { + if (activated) { + throw new IllegalStateException("Device already registered"); + } + MatterDeviceOptions options = activate(); + activated = true; + return client.addEndpoint(deviceType(), primaryItem.getName(), options.label, primaryItem.getName(), + "Type " + primaryItem.getType(), String.valueOf(primaryItem.getName().hashCode()), options.clusters); + } + + public String getName() { + return primaryItem.getName(); + } + + public CompletableFuture setEndpointState(String clusterName, String attributeName, Object state) { + return client.setEndpointState(primaryItem.getName(), clusterName, attributeName, state); + } + + protected MetaDataMapping metaDataMapping(GenericItem item) { + Metadata metadata = metadataRegistry.get(new MetadataKey("matter", item.getUID())); + String label = item.getLabel(); + List attributeList = List.of(); + Map config = new HashMap<>(); + if (metadata != null) { + attributeList = Arrays.stream(metadata.getValue().split(",")).map(String::trim) + .collect(Collectors.toList()); + metadata.getConfiguration().forEach((key, value) -> { + config.put(key.replace('-', '.').trim(), value); + }); + if (config.get("label") instanceof String customLabel) { + label = customLabel; + } + + // convert the value of fixed labels into a cluster attribute + if (config.get("fixedLabels") instanceof String fixedLabels) { + List labelList = parseFixedLabels(fixedLabels); + config.put("fixedLabel.labelList", labelList); + } + } + + if (label == null) { + label = item.getName(); + } + + return new MetaDataMapping(attributeList, config, label); + } + + /** + * This class is used to map the metadata to the endpoint options + */ + class MetaDataMapping { + public final List attributes; + /** + * The config for the item, this will be a mix of custom mapping like "ON=1" and cluster attributes like + * "clusterName.attributeName=2" + */ + public final Map config; + /** + * The label for the item + */ + public final String label; + + public MetaDataMapping(List attributes, Map config, String label) { + this.attributes = attributes; + this.config = config; + this.label = label; + } + + /** + * Get the attribute options from the config, this filters the entries to just keys like + * "clusterName.attributeName" + * + * @return + */ + public Map getAttributeOptions() { + return config.entrySet().stream().filter(entry -> entry.getKey().contains(".")) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + } + + class MatterDeviceOptions { + public final Map> clusters; + public final String label; + + public MatterDeviceOptions(Map attributes, String label) { + this.clusters = mapClusterAttributes(attributes); + this.label = label; + } + } + + Map> mapClusterAttributes(Map clusterAttributes) { + Map> returnMap = new HashMap<>(); + clusterAttributes.forEach((key, value) -> { + String[] parts = key.split("\\."); + if (parts.length != 2) { + throw new IllegalArgumentException("Key must be in the format 'clusterName.attributeName'"); + } + String clusterName = parts[0]; + String attributeName = parts[1]; + + // Get or create the child map for the clusterName + Map attributes = returnMap.computeIfAbsent(clusterName, k -> new HashMap<>()); + + // Update the attributeName with the value + if (attributes != null) { + attributes.put(attributeName, value); + } + }); + return returnMap; + } + + private List parseFixedLabels(String labels) { + Map keyValueMap = Arrays.stream(labels.split(",")).map(pair -> pair.trim().split("=", 2)) + .filter(parts -> parts.length == 2) + .collect(Collectors.toMap(parts -> parts[0].trim(), parts -> parts[1].trim())); + return keyValueMap.entrySet().stream().map(entry -> new KeyValue(entry.getKey(), entry.getValue())).toList(); + } + + class KeyValue { + public final String label; + public final String value; + + public KeyValue(String label, String value) { + this.label = label; + this.value = value; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/HumiditySensorDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/HumiditySensorDevice.java new file mode 100644 index 00000000000..97042535db3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/HumiditySensorDevice.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.RelativeHumidityMeasurementCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.types.State; + +/** + * The {@link HumiditySensorDevice} is a device that represents a Humidity Sensor. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class HumiditySensorDevice extends GenericDevice { + private static final BigDecimal HUMIDITY_MULTIPLIER = new BigDecimal(100); + + public HumiditySensorDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "HumiditySensor"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + attributeMap.put( + RelativeHumidityMeasurementCluster.CLUSTER_PREFIX + "." + + RelativeHumidityMeasurementCluster.ATTRIBUTE_MEASURED_VALUE, + toMatterValue(primaryItem.getState())); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void updateState(Item item, State state) { + setEndpointState(RelativeHumidityMeasurementCluster.CLUSTER_PREFIX, + RelativeHumidityMeasurementCluster.ATTRIBUTE_MEASURED_VALUE, toMatterValue(state)); + } + + private int toMatterValue(@Nullable State humidity) { + BigDecimal value = new BigDecimal(0); + if (humidity instanceof QuantityType quantityType) { + value = quantityType.toBigDecimal(); + } + if (humidity instanceof Number number) { + value = BigDecimal.valueOf(number.doubleValue()); + } + return value.setScale(2, RoundingMode.CEILING).multiply(HUMIDITY_MULTIPLIER).intValue(); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OccupancySensorDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OccupancySensorDevice.java new file mode 100644 index 00000000000..f9a9c9a27cd --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OccupancySensorDevice.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OccupancySensingCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.types.State; + +import com.google.gson.JsonObject; + +/** + * The {@link OccupancySensorDevice} is a device that represents an Occupancy Sensor. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class OccupancySensorDevice extends GenericDevice { + + public OccupancySensorDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "OccupancySensor"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + attributeMap.put(OccupancySensingCluster.CLUSTER_PREFIX + "." + OccupancySensingCluster.ATTRIBUTE_OCCUPANCY, + occupiedState(primaryItem.getState())); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void updateState(Item item, State state) { + setEndpointState(OccupancySensingCluster.CLUSTER_PREFIX, OccupancySensingCluster.ATTRIBUTE_OCCUPANCY, + occupiedState(primaryItem.getState())); + } + + private JsonObject occupiedState(State state) { + boolean occupied = false; + if (state instanceof OnOffType onOffType) { + occupied = onOffType == OnOffType.ON; + } + if (state instanceof OpenClosedType openClosedType) { + occupied = openClosedType == OpenClosedType.OPEN; + } + JsonObject stateJson = new JsonObject(); + stateJson.addProperty("occupied", occupied); + return stateJson; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OnOffLightDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OnOffLightDevice.java new file mode 100644 index 00000000000..f2cf8a966b0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OnOffLightDevice.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.types.State; + +/** + * The {@link OnOffLightDevice} is a device that represents an On/Off Light. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class OnOffLightDevice extends GenericDevice { + + public OnOffLightDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "OnOffLight"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + switch (attributeName) { + case OnOffCluster.ATTRIBUTE_ON_OFF: { + if (primaryItem instanceof GroupItem groupItem) { + groupItem.send(OnOffType.from(Boolean.valueOf(data.toString()))); + } else if (primaryItem instanceof SwitchItem switchItem) { + switchItem.send(OnOffType.from(Boolean.valueOf(data.toString()))); + } + } + break; + case LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL: { + OnOffType onOff = OnOffType.from(((Double) data).intValue() > 0); + if (primaryItem instanceof GroupItem groupItem) { + groupItem.send(onOff); + } else if (primaryItem instanceof SwitchItem switchItem) { + switchItem.send(onOff); + } + } + break; + default: + break; + } + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + attributeMap.put(OnOffCluster.CLUSTER_PREFIX + "." + OnOffCluster.ATTRIBUTE_ON_OFF, Optional + .ofNullable(primaryItem.getStateAs(OnOffType.class)).orElseGet(() -> OnOffType.OFF) == OnOffType.ON); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void updateState(Item item, State state) { + if (state instanceof HSBType hsb) { + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, + hsb.getBrightness().intValue() > 0 ? true : false); + } else if (state instanceof PercentType percentType) { + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, + percentType.intValue() > 0 ? true : false); + } else if (state instanceof OnOffType onOffType) { + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, onOffType == OnOffType.ON); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OnOffPlugInUnitDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OnOffPlugInUnitDevice.java new file mode 100644 index 00000000000..f11f01389e8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/OnOffPlugInUnitDevice.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.MetadataRegistry; + +/** + * The {@link OnOffPlugInUnitDevice} is a device that represents an On/Off Plug-In Unit. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class OnOffPlugInUnitDevice extends OnOffLightDevice { + + public OnOffPlugInUnitDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "OnOffPlugInUnit"; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/TemperatureSensorDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/TemperatureSensorDevice.java new file mode 100644 index 00000000000..749c6832c7d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/TemperatureSensorDevice.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.TemperatureMeasurementCluster; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.types.State; + +/** + * The {@link TemperatureSensorDevice} is a device that represents a Temperature Sensor. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class TemperatureSensorDevice extends GenericDevice { + + public TemperatureSensorDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "TemperatureSensor"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + State state = primaryItem.getState(); + Integer value = ValueUtils.temperatureToValue(state); + attributeMap.put(TemperatureMeasurementCluster.CLUSTER_PREFIX + "." + + TemperatureMeasurementCluster.ATTRIBUTE_MEASURED_VALUE, value == null ? 0 : value); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + } + + @Override + public void updateState(Item item, State state) { + Integer value = ValueUtils.temperatureToValue(state); + if (value != null) { + setEndpointState(TemperatureMeasurementCluster.CLUSTER_PREFIX, + TemperatureMeasurementCluster.ATTRIBUTE_MEASURED_VALUE, value); + } else { + logger.debug("Could not convert {} to matter value", state.toString()); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ThermostatDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ThermostatDevice.java new file mode 100644 index 00000000000..2b37bf0df30 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/ThermostatDevice.java @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.HashMap; +import java.util.Map; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThermostatCluster; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.items.StringItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The {@link ThermostatDevice} is a device that represents a Thermostat. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ThermostatDevice extends GenericDevice { + private final Map itemMap = new HashMap<>(); + private final Map attributeToItemNameMap = new HashMap<>(); + private final SystemModeMapper systemModeMapper = new SystemModeMapper(); + + public ThermostatDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "Thermostat"; + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + String pathName = clusterName + "." + attributeName; + String itemUid = attributeToItemNameMap.get(pathName); + if (itemUid != null) { + GenericItem item = itemMap.get(itemUid); + if (item != null) { + switch (attributeName) { + case ThermostatCluster.ATTRIBUTE_OCCUPIED_HEATING_SETPOINT: + case ThermostatCluster.ATTRIBUTE_OCCUPIED_COOLING_SETPOINT: + if (item instanceof NumberItem numberItem) { + QuantityType t = ValueUtils + .valueToTemperature(Float.valueOf(data.toString()).intValue()); + numberItem.send(t); + } + break; + case ThermostatCluster.ATTRIBUTE_SYSTEM_MODE: + try { + int mode = ((Double) data).intValue(); + String mappedMode = systemModeMapper.toCustomValue(mode); + if (item instanceof NumberItem numberItem) { + numberItem.send(new DecimalType(mappedMode)); + } else if (item instanceof StringItem stringItem) { + stringItem.send(new StringType(mappedMode)); + } else if (item instanceof SwitchItem switchItem) { + switchItem.send(OnOffType.from(mode > 0)); + } + } catch (SystemModeMappingException e) { + logger.debug("Could not convert {} to custom value", data); + } + break; + case OnOffCluster.ATTRIBUTE_ON_OFF: + try { + if (data instanceof Boolean onOff) { + String mappedMode = onOff ? systemModeMapper.onToCustomValue() + : systemModeMapper.toCustomValue(0); + if (item instanceof NumberItem) { + item.setState(new DecimalType(mappedMode)); + } else { + item.setState(new StringType(mappedMode)); + } + } + } catch (SystemModeMappingException e) { + logger.debug("Could not convert {} to custom value", data); + } + break; + default: + break; + } + } + } + } + + @Override + public void updateState(Item item, State state) { + attributeToItemNameMap.forEach((attribute, itemUid) -> { + if (itemUid.equals(item.getUID())) { + // we need to do conversion here + String[] pair = attribute.split("\\."); + if (pair.length != 2) { + logger.debug("Unknown attribute format {}", attribute); + return; + } + String clusterName = pair[0]; + String attributeName = pair[1]; + switch (attributeName) { + case ThermostatCluster.ATTRIBUTE_LOCAL_TEMPERATURE: + case ThermostatCluster.ATTRIBUTE_OUTDOOR_TEMPERATURE: + case ThermostatCluster.ATTRIBUTE_OCCUPIED_HEATING_SETPOINT: + case ThermostatCluster.ATTRIBUTE_OCCUPIED_COOLING_SETPOINT: + Integer value = ValueUtils.temperatureToValue(state); + if (value != null) { + logger.debug("Setting {} to {}", attributeName, value); + setEndpointState(clusterName, attributeName, value); + } else { + logger.debug("Could not convert {} to matter value", state.toString()); + } + break; + case ThermostatCluster.ATTRIBUTE_SYSTEM_MODE: + try { + int mode = systemModeMapper.fromCustomValue(state.toString()).value; + setEndpointState(clusterName, attributeName, mode); + setEndpointState(OnOffCluster.CLUSTER_PREFIX, OnOffCluster.ATTRIBUTE_ON_OFF, mode > 0); + } catch (SystemModeMappingException e) { + logger.debug("Could not convert {} to matter value", state.toString()); + } + break; + default: + break; + } + } + }); + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + Map attributeMap = new HashMap<>(); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + // add any settings for attributes from config, like thermostat.minHeatSetpointLimit=0 + attributeMap.putAll(primaryMetadata.getAttributeOptions()); + for (Item member : ((GroupItem) primaryItem).getAllMembers()) { + if (member instanceof GenericItem genericMember) { + MetaDataMapping metadata = metaDataMapping(genericMember); + State state = genericMember.getState(); + for (String attribute : metadata.attributes) { + String[] pair = attribute.split("\\."); + if (pair.length != 2) { + logger.debug("Unknown attribute format {}", attribute); + continue; + } + String attributeName = pair[1]; + switch (attributeName) { + case ThermostatCluster.ATTRIBUTE_LOCAL_TEMPERATURE: + case ThermostatCluster.ATTRIBUTE_OUTDOOR_TEMPERATURE: + case ThermostatCluster.ATTRIBUTE_OCCUPIED_HEATING_SETPOINT: + case ThermostatCluster.ATTRIBUTE_OCCUPIED_COOLING_SETPOINT: + if (state instanceof UnDefType) { + attributeMap.put(attribute, 0); + } else { + Integer value = ValueUtils.temperatureToValue(state); + attributeMap.put(attribute, value != null ? value : 0); + } + break; + case ThermostatCluster.ATTRIBUTE_SYSTEM_MODE: + try { + systemModeMapper.initializeMappings(metadata.config); + int mode = systemModeMapper.fromCustomValue(state.toString()).value; + attributeMap.put(attribute, mode); + attributeMap.put(OnOffCluster.CLUSTER_PREFIX + "." + OnOffCluster.ATTRIBUTE_ON_OFF, + mode > 0); + } catch (SystemModeMappingException e) { + logger.debug("Could not convert {} to matter value", state.toString()); + } + break; + default: + continue; + } + if (!itemMap.containsKey(genericMember.getUID())) { + itemMap.put(genericMember.getUID(), genericMember); + genericMember.addStateChangeListener(this); + } + // add any settings for attributes from config, like thermostat.minHeatSetpointLimit=0 + attributeMap.putAll(metadata.getAttributeOptions()); + attributeToItemNameMap.put(attribute, genericMember.getUID()); + } + } + } + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + attributeToItemNameMap.clear(); + primaryItem.removeStateChangeListener(this); + itemMap.forEach((uid, item) -> { + ((GenericItem) item).removeStateChangeListener(this); + }); + itemMap.clear(); + } + + class SystemModeMapper { + private final Map intToCustomMap = new HashMap<>(); + private final Map customToEnumMap = new HashMap<>(); + private @Nullable String onMode = null; + + public SystemModeMapper() { + Map mappings = new HashMap<>(); + ThermostatCluster.SystemModeEnum[] modes = ThermostatCluster.SystemModeEnum.values(); + for (ThermostatCluster.SystemModeEnum mode : modes) { + mappings.put(mode.name(), mode.getValue()); + } + mappings.put("ON", ThermostatCluster.SystemModeEnum.AUTO.getValue()); // this is a special case for ON + initializeMappings(mappings); + } + + public SystemModeMapper(Map mappings) { + initializeMappings(mappings); + } + + private void initializeMappings(Map mappings) { + if (mappings.isEmpty()) { + return; + } + + // don't bother mapping if there's no OFF + if (!mappings.containsKey("OFF")) { + return; + } + + if (!mappings.containsKey("ON")) { + Object onObject = mappings.get("COOL"); + if (onObject != null) { + mappings.put("ON", onObject); + } else { + onObject = mappings.get("HEAT"); + if (onObject != null) { + mappings.put("ON", onObject); + } + } + } + + intToCustomMap.clear(); + customToEnumMap.clear(); + for (Map.Entry entry : mappings.entrySet()) { + String customKey = entry.getKey().trim(); + Object valueObj = entry.getValue(); + String customValue = valueObj.toString().trim(); + + if ("ON".equals(customKey)) { + onMode = customValue; + continue; + } + + try { + ThermostatCluster.SystemModeEnum mode = ThermostatCluster.SystemModeEnum.valueOf(customKey); + intToCustomMap.put(mode.value, customValue); + customToEnumMap.put(customValue, mode); + } catch (IllegalArgumentException e) { + // ignore unknown values + } + } + } + + public String toCustomValue(int modeValue) throws SystemModeMappingException { + String value = intToCustomMap.get(modeValue); + if (value == null) { + throw new SystemModeMappingException("No mapping for mode: " + modeValue); + } + return value; + } + + public String onToCustomValue() throws SystemModeMappingException { + String value = this.onMode; + if (value == null) { + value = intToCustomMap.get(ThermostatCluster.SystemModeEnum.AUTO.value); + } + if (value == null) { + value = ThermostatCluster.SystemModeEnum.AUTO.getValue().toString(); + } + return value; + } + + public ThermostatCluster.SystemModeEnum fromCustomValue(String customValue) throws SystemModeMappingException { + if ("ON".equals(customValue)) { + String onMode = this.onMode; + if (onMode != null) { + return fromCustomValue(onMode); + } else { + return ThermostatCluster.SystemModeEnum.AUTO; + } + } + + ThermostatCluster.SystemModeEnum value = customToEnumMap.get(customValue); + if (value == null) { + throw new SystemModeMappingException("No mapping for custom value: " + customValue); + } + return value; + } + } + + class SystemModeMappingException extends Exception { + private static final long serialVersionUID = 1L; + + public SystemModeMappingException(String message) { + super(message); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/WindowCoveringDevice.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/WindowCoveringDevice.java new file mode 100644 index 00000000000..8176a84543e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/bridge/devices/WindowCoveringDevice.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import java.util.AbstractMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WindowCoveringCluster; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Item; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.DimmerItem; +import org.openhab.core.library.items.RollershutterItem; +import org.openhab.core.library.items.StringItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.types.State; + +/** + * The {@link WindowCoveringDevice} is a device that represents a Window Covering. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class WindowCoveringDevice extends GenericDevice { + private ScheduledExecutorService operationalStateScheduler = Executors.newSingleThreadScheduledExecutor(); + private @Nullable ScheduledFuture operationalStateTimer = null; + private @Nullable Integer lastTargetPercent = null; + + public WindowCoveringDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, GenericItem item) { + super(metadataRegistry, client, item); + } + + @Override + public String deviceType() { + return "WindowCovering"; + } + + @Override + protected MatterDeviceOptions activate() { + primaryItem.addStateChangeListener(this); + MetaDataMapping primaryMetadata = metaDataMapping(primaryItem); + Map attributeMap = primaryMetadata.getAttributeOptions(); + attributeMap.put( + WindowCoveringCluster.CLUSTER_PREFIX + "." + + WindowCoveringCluster.ATTRIBUTE_CURRENT_POSITION_LIFT_PERCENT100THS, + itemStateToPercent(primaryItem.getState()) * 100); + return new MatterDeviceOptions(attributeMap, primaryMetadata.label); + } + + @Override + public void dispose() { + primaryItem.removeStateChangeListener(this); + cancelTimer(); + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + switch (attributeName) { + case WindowCoveringCluster.ATTRIBUTE_TARGET_POSITION_LIFT_PERCENT100THS: + PercentType percentType = new PercentType((int) ((Double) data / 100)); + lastTargetPercent = percentType.intValue(); + int currentPercent = itemStateToPercent(primaryItem.getState()); + if (currentPercent >= 0) { + updateOperationalStatus(currentPercent); + } + // do logic to sen op state + boolean open = percentType.intValue() == 0; + Metadata primaryItemMetadata = this.primaryItemMetadata; + String key = open ? "OPEN" : "CLOSED"; + if (primaryItem instanceof GroupItem groupItem) { + groupItem.send(percentType); + } else if (primaryItem instanceof DimmerItem dimmerItem) { + dimmerItem.send(percentType); + } else if (primaryItem instanceof RollershutterItem rollerShutterItem) { + if (percentType.intValue() == 100) { + rollerShutterItem.send(UpDownType.DOWN); + } else if (percentType.intValue() == 0) { + rollerShutterItem.send(UpDownType.UP); + } else { + rollerShutterItem.send(percentType); + } + } else if (primaryItem instanceof SwitchItem switchItem) { + boolean invert = false; + if (primaryItemMetadata != null) { + Object invertObject = primaryItemMetadata.getConfiguration().getOrDefault("invert", false); + if (invertObject instanceof Boolean invertValue) { + invert = invertValue; + } + } + switchItem.send(OnOffType.from(invert ? open ? "ON" : "OFF" : open ? "OFF" : "ON")); + } else if (primaryItem instanceof StringItem stringItem) { + Object value = key; + if (primaryItemMetadata != null) { + value = primaryItemMetadata.getConfiguration().getOrDefault(key, key); + } + stringItem.send(new StringType(value.toString())); + } + break; + case WindowCoveringCluster.ATTRIBUTE_OPERATIONAL_STATUS: + if (data instanceof AbstractMap treeMap) { + @SuppressWarnings("unchecked") + AbstractMap map = (AbstractMap) treeMap; + if (map.get("global") instanceof Integer value) { + if (WindowCoveringCluster.MovementStatus.STOPPED.getValue().equals(value) + && primaryItem instanceof RollershutterItem rollerShutterItem) { + rollerShutterItem.send(StopMoveType.STOP); + cancelTimer(); + lastTargetPercent = null; + // will send stop back + updateOperationalStatus(0); + } + } + } + break; + default: + break; + } + } + + @Override + public void updateState(Item item, State state) { + int localPercent = itemStateToPercent(state); + if (localPercent >= 0) { + try { + setEndpointState(WindowCoveringCluster.CLUSTER_PREFIX, + WindowCoveringCluster.ATTRIBUTE_CURRENT_POSITION_LIFT_PERCENT100THS, localPercent * 100).get(); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Could not set state", e); + return; + } + cancelTimer(); + final Integer lp = localPercent; + this.operationalStateTimer = operationalStateScheduler.schedule(() -> updateOperationalStatus(lp), 1000, + TimeUnit.MILLISECONDS); + } + } + + private int itemStateToPercent(State state) { + int localPercent = 0; + if (state instanceof PercentType percentType) { + localPercent = percentType.intValue(); + } else if (state instanceof OpenClosedType openClosedType) { + localPercent = openClosedType == OpenClosedType.OPEN ? 0 : 100; + } else if (state instanceof OnOffType onOffType) { + Metadata primaryItemMetadata = this.primaryItemMetadata; + boolean invert = false; + if (primaryItemMetadata != null) { + logger.debug("primaryItemMetadata: {}", primaryItemMetadata); + Object invertObject = primaryItemMetadata.getConfiguration().getOrDefault("invert", false); + if (invertObject instanceof Boolean invertValue) { + logger.debug("invertObject: {}", invertObject); + invert = invertValue; + } + } + localPercent = invert ? onOffType == OnOffType.ON ? 0 : 100 : onOffType == OnOffType.ON ? 100 : 0; + } else if (state instanceof StringType stringType) { + Metadata primaryItemMetadata = this.primaryItemMetadata; + if (primaryItemMetadata != null) { + Object openValue = primaryItemMetadata.getConfiguration().get("OPEN"); + Object closeValue = primaryItemMetadata.getConfiguration().get("CLOSED"); + if (openValue instanceof String && closeValue instanceof String) { + if (stringType.equals(openValue)) { + localPercent = 0; + } else if (stringType.equals(closeValue)) { + localPercent = 100; + } + } + } + } + return localPercent; + } + + private void updateOperationalStatus(Integer localPercent) { + Integer lastTargetPercent = this.lastTargetPercent; + WindowCoveringCluster.MovementStatus status = WindowCoveringCluster.MovementStatus.STOPPED; + if (lastTargetPercent != null) { + if (lastTargetPercent < localPercent) { + status = WindowCoveringCluster.MovementStatus.CLOSING; + } else if (lastTargetPercent > localPercent) { + status = WindowCoveringCluster.MovementStatus.OPENING; + } else { + this.lastTargetPercent = null; + } + } + AbstractMap t = new LinkedHashMap(); + t.put("global", status.getValue()); + t.put("lift", status.getValue()); + setEndpointState(WindowCoveringCluster.CLUSTER_PREFIX, WindowCoveringCluster.ATTRIBUTE_OPERATIONAL_STATUS, t); + } + + private void cancelTimer() { + ScheduledFuture operationalStateTimer = this.operationalStateTimer; + if (operationalStateTimer != null) { + operationalStateTimer.cancel(true); + } + this.operationalStateTimer = null; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/AttributeListener.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/AttributeListener.java new file mode 100644 index 00000000000..f9b30f2ab08 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/AttributeListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; + +/** + * A listener for attribute changes + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface AttributeListener { + + public void onEvent(AttributeChangedMessage message); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/EventTriggeredListener.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/EventTriggeredListener.java new file mode 100644 index 00000000000..500bd8c1071 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/EventTriggeredListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; + +/** + * A listener for event triggered messages + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface EventTriggeredListener { + + public void onEvent(EventTriggeredMessage message); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterClientListener.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterClientListener.java new file mode 100644 index 00000000000..0c69216160c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterClientListener.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeDataMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeStateMessage; + +/** + * A listener for Matter client events + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface MatterClientListener { + public void onDisconnect(String reason); + + public void onConnect(); + + public void onReady(); + + public void onEvent(NodeStateMessage message); + + public void onEvent(AttributeChangedMessage message); + + public void onEvent(EventTriggeredMessage message); + + public void onEvent(BridgeEventMessage message); + + public void onEvent(NodeDataMessage message); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterWebsocketClient.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterWebsocketClient.java new file mode 100644 index 00000000000..7f137f8a42b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterWebsocketClient.java @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigInteger; +import java.net.URI; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketListener; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.Node; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster.OctetString; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ClusterRegistry; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventAttributeChanged; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventMessage; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventTriggered; +import org.openhab.binding.matter.internal.client.dto.ws.Event; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Message; +import org.openhab.binding.matter.internal.client.dto.ws.NodeDataMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeStateMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.binding.matter.internal.client.dto.ws.Request; +import org.openhab.binding.matter.internal.client.dto.ws.Response; +import org.openhab.binding.matter.internal.client.dto.ws.TriggerEvent; +import org.openhab.core.common.ThreadPoolManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; + +/** + * A client for the Matter WebSocket API for communicating with a Matter controller + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterWebsocketClient implements WebSocketListener, MatterWebsocketService.NodeProcessListener { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + private static final int BUFFER_SIZE = 1048576 * 2; // 2 Mb + private static final int REQUEST_TIMEOUT_SECONDS = 60 * 3; // 3 minutes + + private final ScheduledExecutorService scheduler = ThreadPoolManager + .getScheduledPool("matter.MatterWebsocketClient"); + + protected final Gson gson = new GsonBuilder().registerTypeAdapter(Node.class, new NodeDeserializer()) + .registerTypeAdapter(BigInteger.class, new BigIntegerSerializer()) + .registerTypeHierarchyAdapter(BaseCluster.MatterEnum.class, new MatterEnumDeserializer()) + .registerTypeAdapter(AttributeChangedMessage.class, new AttributeChangedMessageDeserializer()) + .registerTypeAdapter(EventTriggeredMessage.class, new EventTriggeredMessageDeserializer()) + .registerTypeAdapter(OctetString.class, new OctetStringDeserializer()) + .registerTypeAdapter(OctetString.class, new OctetStringSerializer()).create(); + + protected final WebSocketClient client = new WebSocketClient(); + protected final ConcurrentHashMap> pendingRequests = new ConcurrentHashMap<>(); + protected final CopyOnWriteArrayList clientListeners = new CopyOnWriteArrayList<>(); + + @Nullable + private Session session; + @Nullable + Map connectionParameters; + + @Nullable + private MatterWebsocketService wss; + + /** + * Connect to a local Matter controller running on this host in openHAB, primarily use case + * + * @param nodeId + * @param storagePath + * @param controllerName + * @throws Exception + */ + public void connectWhenReady(MatterWebsocketService wss, Map connectionParameters) { + this.connectionParameters = connectionParameters; + this.wss = wss; + wss.addProcessListener(this); + } + + /** + * Disconnect from the controller + */ + public void disconnect() { + Session session = this.session; + try { + pendingRequests.forEach((id, future) -> { + if (!future.isDone()) { + future.completeExceptionally(new Exception("Client disconnected")); + } + }); + pendingRequests.clear(); + + if (session != null && session.isOpen()) { + session.disconnect(); + session.close(); + session = null; + } + } catch (IOException e) { + logger.debug("Error trying to disconnect", e); + } finally { + try { + client.stop(); + } catch (Exception e) { + logger.debug("Error closing Web Socket", e); + } + MatterWebsocketService wss = this.wss; + if (wss != null) { + wss.removeProcessListener(this); + } + } + } + + /** + * Add a listener to the client + * + * @param listener + */ + public void addListener(MatterClientListener listener) { + clientListeners.add(listener); + } + + /** + * Remove a listener from the client + * + * @param listener + */ + public void removeListener(MatterClientListener listener) { + clientListeners.remove(listener); + } + + /** + * Check if the client is connected to the controller + * + * @return + */ + public boolean isConnected() { + Session session = this.session; + return session != null && session.isOpen(); + } + + /** + * Send a generic command to the controller + * + * @param namespace + * @param functionName + * @param objects + * @return + */ + public CompletableFuture genericCommand(String namespace, String functionName, + @Nullable Object... objects) { + CompletableFuture future = sendMessage(namespace, functionName, objects); + return future.thenApply(obj -> obj.toString()); + } + + @Override + public void onNodeExit(int exitCode) { + } + + @Override + public void onNodeReady(int port) { + logger.debug("onNodeReady port {}", port); + if (isConnected()) { + logger.debug("Already connected, aborting!"); + return; + } + try { + connectWebsocket("localhost", port); + } catch (Exception e) { + disconnect(); + logger.debug("Could not connect", e); + for (MatterClientListener listener : clientListeners) { + String msg = e.getLocalizedMessage(); + listener.onDisconnect(msg != null ? msg : "Exception connecting"); + } + } + } + + @Override + public void onWebSocketConnect(@Nullable Session session) { + if (session != null) { + final WebSocketPolicy currentPolicy = session.getPolicy(); + currentPolicy.setInputBufferSize(BUFFER_SIZE); + currentPolicy.setMaxTextMessageSize(BUFFER_SIZE); + currentPolicy.setMaxBinaryMessageSize(BUFFER_SIZE); + this.session = session; + for (MatterClientListener listener : clientListeners) { + listener.onConnect(); + } + } + } + + @Override + public void onWebSocketText(@Nullable String msg) { + logger.debug("onWebSocketText {}", msg); + scheduler.submit(() -> { + Message message = gson.fromJson(msg, Message.class); + if (message == null) { + logger.debug("invalid Message"); + return; + } + if ("response".equals(message.type)) { + Response response = gson.fromJson(message.message, Response.class); + if (response == null) { + logger.debug("invalid response Message"); + return; + } + CompletableFuture future = pendingRequests.remove(response.id); + if (future == null) { + logger.debug("no future for response id {}, type {} , did the request timeout?", response.id, + response.type); + return; + } + logger.debug("result type: {} ", response.type); + if (!"resultSuccess".equals(response.type)) { + future.completeExceptionally(new Exception(response.error)); + } else { + future.complete(response.result); + } + } else if ("event".equals(message.type)) { + Event event = gson.fromJson(message.message, Event.class); + if (event == null) { + logger.debug("invalid Event"); + return; + } + switch (event.type) { + case "attributeChanged": + logger.debug("attributeChanged message {}", event.data); + AttributeChangedMessage changedMessage = gson.fromJson(event.data, + AttributeChangedMessage.class); + if (changedMessage == null) { + logger.debug("invalid AttributeChangedMessage"); + return; + } + for (MatterClientListener listener : clientListeners) { + try { + listener.onEvent(changedMessage); + } catch (Exception e) { + logger.debug("Error notifying listener", e); + } + } + break; + case "eventTriggered": + logger.debug("eventTriggered message {}", event.data); + EventTriggeredMessage triggeredMessage = gson.fromJson(event.data, EventTriggeredMessage.class); + if (triggeredMessage == null) { + logger.debug("invalid EventTriggeredMessage"); + return; + } + for (MatterClientListener listener : clientListeners) { + try { + listener.onEvent(triggeredMessage); + } catch (Exception e) { + logger.debug("Error notifying listener", e); + } + } + break; + case "nodeStateInformation": + logger.debug("nodeStateInformation message {}", event.data); + NodeStateMessage nodeStateMessage = gson.fromJson(event.data, NodeStateMessage.class); + if (nodeStateMessage == null) { + logger.debug("invalid NodeStateMessage"); + return; + } + for (MatterClientListener listener : clientListeners) { + try { + listener.onEvent(nodeStateMessage); + } catch (Exception e) { + logger.debug("Error notifying listener", e); + } + } + break; + case "nodeData": + logger.debug("nodeData message {}", event.data); + Node node = gson.fromJson(event.data, Node.class); + if (node == null) { + logger.debug("invalid nodeData"); + return; + } + for (MatterClientListener listener : clientListeners) { + try { + listener.onEvent(new NodeDataMessage(node)); + } catch (Exception e) { + logger.debug("Error notifying listener", e); + } + } + break; + case "bridgeEvent": + logger.debug("bridgeEvent message {}", event.data); + BridgeEventMessage bridgeEventMessage = gson.fromJson(event.data, BridgeEventMessage.class); + + if (bridgeEventMessage == null) { + logger.debug("invalid bridgeEvent"); + return; + } + + switch (bridgeEventMessage.type) { + case "attributeChanged": + bridgeEventMessage = gson.fromJson(event.data, BridgeEventAttributeChanged.class); + break; + case "eventTriggered": + bridgeEventMessage = gson.fromJson(event.data, BridgeEventTriggered.class); + break; + } + + if (bridgeEventMessage == null) { + logger.debug("invalid bridgeEvent subtype"); + return; + } + + for (MatterClientListener listener : clientListeners) { + try { + listener.onEvent(bridgeEventMessage); + } catch (Exception e) { + logger.debug("Error notifying listener", e); + } + } + break; + case "ready": + for (MatterClientListener listener : clientListeners) { + listener.onReady(); + } + break; + default: + break; + } + } + }); + } + + @Override + public void onWebSocketClose(int statusCode, @Nullable String reason) { + logger.debug("onWebSocketClose {} {}", statusCode, reason); + for (MatterClientListener listener : clientListeners) { + listener.onDisconnect(reason != null ? reason : "Code " + statusCode); + } + } + + @Override + public void onWebSocketError(@Nullable Throwable cause) { + logger.debug("onWebSocketError", cause); + } + + @Override + public void onWebSocketBinary(byte @Nullable [] payload, int offset, int len) { + logger.debug("onWebSocketBinary data, not supported"); + } + + protected CompletableFuture sendMessage(String namespace, String functionName, + @Nullable Object args[]) { + return sendMessage(namespace, functionName, args, REQUEST_TIMEOUT_SECONDS); + } + + protected CompletableFuture sendMessage(String namespace, String functionName, @Nullable Object args[], + int timeoutSeconds) { + if (timeoutSeconds <= 0) { + timeoutSeconds = REQUEST_TIMEOUT_SECONDS; + } + CompletableFuture responseFuture = new CompletableFuture<>(); + + Session session = this.session; + if (session == null) { + logger.debug("Could not send {} {} : no valid session", namespace, functionName); + return responseFuture; + } + String requestId = UUID.randomUUID().toString(); + pendingRequests.put(requestId, responseFuture); + Request message = new Request(requestId, namespace, functionName, args); + String jsonMessage = gson.toJson(message); + logger.debug("sendMessage: {}", jsonMessage); + session.getRemote().sendStringByFuture(jsonMessage); + + // timeout handling + scheduler.schedule(() -> { + CompletableFuture future = pendingRequests.remove(requestId); + if (future != null && !future.isDone()) { + future.completeExceptionally(new TimeoutException(String.format( + "Request %s:%s timed out after %d seconds", namespace, functionName, REQUEST_TIMEOUT_SECONDS))); + } + }, timeoutSeconds, TimeUnit.SECONDS); + + return responseFuture; + } + + private void connectWebsocket(String host, int port) throws Exception { + String dest = "ws://" + host + ":" + port; + Map connectionParameters = this.connectionParameters; + if (connectionParameters != null) { + dest += "?" + connectionParameters.entrySet().stream() + .map((Map.Entry entry) -> URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8) + + "=" + URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)) + .collect(Collectors.joining("&")); + } + + logger.debug("Connecting {}", dest); + WebSocketClient client = new WebSocketClient(); + client.setMaxIdleTimeout(Long.MAX_VALUE); + client.start(); + URI uri = new URI(dest); + client.connect(this, uri, new ClientUpgradeRequest()).get(); + } + + @NonNullByDefault({}) + class NodeDeserializer implements JsonDeserializer { + @Override + public @Nullable Node deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObjectNode = json.getAsJsonObject(); + Node node = new Node(); + node.id = jsonObjectNode.get("id").getAsBigInteger(); + + // Deserialize root endpoint + JsonObject rootEndpointJson = jsonObjectNode.getAsJsonObject("rootEndpoint"); + Endpoint rootEndpoint = deserializeEndpoint(rootEndpointJson, context); + node.rootEndpoint = rootEndpoint; + + return node; + } + + private Endpoint deserializeEndpoint(JsonObject endpointJson, JsonDeserializationContext context) { + Endpoint endpoint = new Endpoint(); + endpoint.number = endpointJson.get("number").getAsInt(); + endpoint.clusters = new HashMap<>(); + logger.trace("deserializeEndpoint {}", endpoint.number); + + // Deserialize clusters + JsonObject clustersJson = endpointJson.getAsJsonObject("clusters"); + Set> clusterEntries = clustersJson.entrySet(); + for (Map.Entry clusterEntry : clusterEntries) { + String clusterName = clusterEntry.getKey(); + JsonElement clusterElement = clusterEntry.getValue(); + logger.trace("Cluster {}", clusterEntry); + try { + Class clazz = Class.forName(BaseCluster.class.getPackageName() + "." + clusterName + "Cluster"); + if (BaseCluster.class.isAssignableFrom(clazz)) { + BaseCluster cluster = context.deserialize(clusterElement, clazz); + deserializeFields(cluster, clusterElement, clazz, context); + endpoint.clusters.put(clusterName, cluster); + logger.trace("deserializeEndpoint adding cluster {} to endpoint {}", clusterName, + endpoint.number); + } + } catch (ClassNotFoundException e) { + logger.debug("Cluster not found: {}", clusterName); + } catch (JsonSyntaxException | IllegalArgumentException | SecurityException + | IllegalAccessException e) { + logger.debug("Exception for cluster {}", clusterName, e); + } + } + + // Deserialize child endpoints + endpoint.children = new ArrayList<>(); + JsonArray childrenJson = endpointJson.getAsJsonArray("children"); + if (childrenJson != null) { + for (JsonElement childElement : childrenJson) { + JsonObject childJson = childElement.getAsJsonObject(); + Endpoint childEndpoint = deserializeEndpoint(childJson, context); + endpoint.children.add(childEndpoint); + } + } + + return endpoint; + } + + private void deserializeFields(Object instance, JsonElement jsonElement, Class clazz, + JsonDeserializationContext context) throws IllegalAccessException { + JsonObject jsonObject = jsonElement.getAsJsonObject(); + for (Map.Entry entry : jsonObject.entrySet()) { + String fieldName = entry.getKey(); + JsonElement element = entry.getValue(); + + try { + Field field = getField(clazz, fieldName); + field.setAccessible(true); + + if (List.class.isAssignableFrom(field.getType())) { + // Handle lists generically + Type fieldType = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; + List list = context.deserialize(element, + TypeToken.getParameterized(List.class, fieldType).getType()); + field.set(instance, list); + } else { + // Handle normal fields + Object fieldValue = context.deserialize(element, field.getType()); + field.set(instance, fieldValue); + } + } catch (NoSuchFieldException e) { + logger.trace("Skipping field {}", fieldName); + } + } + } + + private Field getField(Class clazz, String fieldName) throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw e; + } else { + return getField(superClass, fieldName); + } + } + } + } + + @NonNullByDefault({}) + class AttributeChangedMessageDeserializer implements JsonDeserializer { + @Override + public @Nullable AttributeChangedMessage deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + + Path path = context.deserialize(jsonObject.get("path"), Path.class); + Long version = jsonObject.get("version").getAsLong(); + + JsonElement valueElement = jsonObject.get("value"); + Object value = null; + + // Use ClusterRegistry to find the cluster class + Class clusterClass = ClusterRegistry.CLUSTER_IDS.get(path.clusterId); + if (clusterClass != null) { + try { + // Use reflection to find the field type + Field field = getField(clusterClass, path.attributeName); + if (field != null) { + value = context.deserialize(valueElement, field.getType()); + } + } catch (NoSuchFieldException e) { + logger.debug("Field not found for attribute: {}", path.attributeName, e); + } + } + + if (value == null) { + // Fallback to primitive types if no specific class is found + if (valueElement.isJsonPrimitive()) { + JsonPrimitive primitive = valueElement.getAsJsonPrimitive(); + if (primitive.isNumber()) { + value = primitive.getAsNumber(); + } else if (primitive.isString()) { + value = primitive.getAsString(); + } else if (primitive.isBoolean()) { + value = primitive.getAsBoolean(); + } + } else if (valueElement.isJsonArray()) { + value = context.deserialize(valueElement.getAsJsonArray(), List.class); + } else { + value = valueElement.toString(); + } + } + + return new AttributeChangedMessage(path, version, value); + } + + private Field getField(Class clazz, String fieldName) throws NoSuchFieldException { + try { + return clazz.getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + Class superClass = clazz.getSuperclass(); + if (superClass == null) { + throw e; + } else { + return getField(superClass, fieldName); + } + } + } + } + + /** + * Biginteger types have to be represented as strings in JSON + */ + @NonNullByDefault({}) + class BigIntegerSerializer implements JsonSerializer { + @Override + public JsonElement serialize(BigInteger src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toString()); + } + } + + @NonNullByDefault({}) + class MatterEnumDeserializer implements JsonDeserializer { + @Override + public BaseCluster.MatterEnum deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + int value = json.getAsInt(); + Class rawType = (Class) typeOfT; + + if (BaseCluster.MatterEnum.class.isAssignableFrom(rawType) && rawType.isEnum()) { + @SuppressWarnings("unchecked") + Class enumType = (Class) rawType; + return BaseCluster.MatterEnum.fromValue(enumType, value); + } + + throw new JsonParseException("Unable to deserialize " + typeOfT); + } + } + + @NonNullByDefault({}) + class EventTriggeredMessageDeserializer implements JsonDeserializer { + @Override + public EventTriggeredMessage deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + Path path = context.deserialize(jsonObject.get("path"), Path.class); + JsonArray eventsArray = jsonObject.getAsJsonArray("events"); + + TriggerEvent[] events = new TriggerEvent[eventsArray.size()]; + Class clusterClass = ClusterRegistry.CLUSTER_IDS.get(path.clusterId); + + String eventName = path.eventName; + String className = Character.toUpperCase(eventName.charAt(0)) + eventName.substring(1); + for (int i = 0; i < eventsArray.size(); i++) { + JsonObject eventObject = eventsArray.get(i).getAsJsonObject(); + TriggerEvent event = context.deserialize(eventObject, TriggerEvent.class); + if (clusterClass != null) { + try { + Class eventClass = Class.forName(clusterClass.getName() + "$" + className); + event.data = context.deserialize(eventObject.get("data"), eventClass); + } catch (ClassNotFoundException e) { + logger.debug("Event class not found for event: {}", path.eventName, e); + } + } + events[i] = event; + } + return new EventTriggeredMessage(path, events); + } + } + + @NonNullByDefault({}) + class OctetStringDeserializer implements JsonDeserializer { + @Override + public OctetString deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return new OctetString(json.getAsString()); + } + } + + @NonNullByDefault({}) + class OctetStringSerializer implements JsonSerializer { + @Override + public JsonElement serialize(OctetString src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.toString()); + } + } + + /** + * Get the Gson instance for use in tests + */ + Gson getGson() { + return gson; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterWebsocketService.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterWebsocketService.java new file mode 100644 index 00000000000..318f961da6d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/MatterWebsocketService.java @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.common.ThreadPoolManager; +import org.openhab.core.io.net.http.HttpClientFactory; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A service for managing the Matter Node.js process + * + * @author Dan Cunningham - Initial contribution + */ +@Component(service = MatterWebsocketService.class, scope = ServiceScope.SINGLETON) +@NonNullByDefault +public class MatterWebsocketService { + private final Logger logger = LoggerFactory.getLogger(getClass()); + private static final Pattern LOG_PATTERN = Pattern + .compile("^\\S+\\s+\\S+\\s+(TRACE|DEBUG|INFO|WARN|ERROR)\\s+(\\S+)\\s+(.*)$"); + private static final String MATTER_JS_PATH = "/matter-server/matter.js"; + // Delay before restarting the node process after it exits as well as notifying listeners when it's ready + private static final int STARTUP_DELAY_SECONDS = 5; + // Timeout for shutting down the node process + private static final int SHUTDOWN_TIMEOUT_SECONDS = 3; + private final List processListeners = new ArrayList<>(); + private final ExecutorService executorService = Executors.newFixedThreadPool(4); + private final ScheduledExecutorService scheduler = ThreadPoolManager + .getScheduledPool("matter.MatterWebsocketService"); + private @Nullable ScheduledFuture notifyFuture; + private @Nullable ScheduledFuture restartFuture; + // The path to the Node.js executable (node or node.exe) + private final String nodePath; + // The Node.js process running the matter.js script + private @Nullable Process nodeProcess; + // The state of the service, STARTING, READY, SHUTTING_DOWN + private ServiceState state = ServiceState.STARTING; + // the port the node process is listening on + private int port; + + @Activate + public MatterWebsocketService(final @Reference HttpClientFactory httpClientFactory) throws IOException { + NodeJSRuntimeManager nodeManager = new NodeJSRuntimeManager(httpClientFactory.getCommonHttpClient()); + String nodePath = nodeManager.getNodePath(); + this.nodePath = nodePath; + scheduledStart(0); + } + + @Deactivate + public void deactivate() { + stopNode(); + executorService.shutdown(); + } + + public void restart() { + stopNode(); + scheduledStart(STARTUP_DELAY_SECONDS); + } + + public void addProcessListener(NodeProcessListener listener) { + processListeners.add(listener); + if (state == ServiceState.READY) { + listener.onNodeReady(port); + } + } + + public void removeProcessListener(NodeProcessListener listener) { + processListeners.remove(listener); + } + + public void stopNode() { + logger.debug("stopNode"); + state = ServiceState.SHUTTING_DOWN; + cancelFutures(); + Process nodeProcess = this.nodeProcess; + if (nodeProcess != null && nodeProcess.isAlive()) { + nodeProcess.destroy(); + try { + // Wait for the process to terminate + if (!nodeProcess.waitFor(SHUTDOWN_TIMEOUT_SECONDS, java.util.concurrent.TimeUnit.SECONDS)) { + nodeProcess.destroyForcibly(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.debug("Interrupted while waiting for Node process to stop", e); + } + } + } + + public int getPort() { + return port; + } + + public boolean isReady() { + return state == ServiceState.READY; + } + + private void cancelFutures() { + ScheduledFuture notifyFuture = this.notifyFuture; + if (notifyFuture != null) { + notifyFuture.cancel(true); + } + ScheduledFuture restartFuture = this.restartFuture; + if (restartFuture != null) { + restartFuture.cancel(true); + } + } + + private boolean isRestarting() { + ScheduledFuture restartFuture = this.restartFuture; + return restartFuture != null && !restartFuture.isDone(); + } + + private synchronized void scheduledStart(int delay) { + if (isRestarting()) { + logger.debug("Restart already scheduled, skipping"); + return; + } + logger.debug("Scheduling restart in {} seconds", delay); + restartFuture = scheduler.schedule(() -> { + try { + port = runNodeWithResource(MATTER_JS_PATH); + } catch (IOException e) { + logger.warn("Failed to restart the Matter Node process", e); + } + }, delay, TimeUnit.SECONDS); + } + + private int runNodeWithResource(String resourcePath, String... additionalArgs) throws IOException { + state = ServiceState.STARTING; + Path scriptPath = extractResourceToTempFile(resourcePath); + + port = findAvailablePort(); + List command = new ArrayList<>(); + command.add(nodePath); + command.add(scriptPath.toString()); + command.add("--host"); + command.add("localhost"); + command.add("--port"); + command.add(String.valueOf(port)); + command.addAll(List.of(additionalArgs)); + + ProcessBuilder pb = new ProcessBuilder(command); + nodeProcess = pb.start(); + + // Start output and error stream readers + executorService.submit(this::readOutputStream); + executorService.submit(this::readErrorStream); + + // Wait for the process to exit in a separate thread + executorService.submit(() -> { + int exitCode = -1; + try { + Process nodeProcess = this.nodeProcess; + if (nodeProcess != null) { + exitCode = nodeProcess.waitFor(); + logger.debug("Node process exited with code: {}", exitCode); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.debug("Interrupted while waiting for Node process to exit", e); + } finally { + try { + Files.deleteIfExists(scriptPath); + notifyExitListeners(exitCode); + } catch (IOException e) { + logger.debug("Failed to delete temporary script file", e); + } + + if (state != ServiceState.SHUTTING_DOWN) { + logger.debug("trying to restart, state: {}", state); + scheduledStart(STARTUP_DELAY_SECONDS); + } + } + }); + return port; + } + + private void readOutputStream() { + Process nodeProcess = this.nodeProcess; + if (nodeProcess != null) { + processStream(nodeProcess.getInputStream(), "Error reading Node process output", true); + } + } + + private void readErrorStream() { + Process nodeProcess = this.nodeProcess; + if (nodeProcess != null) { + processStream(nodeProcess.getErrorStream(), "Error reading Node process error stream", false); + } + } + + private void processStream(InputStream inputStream, String errorMessage, boolean triggerNotify) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + // we only want to do this once! + if (state == ServiceState.STARTING && triggerNotify && notifyFuture == null) { + notifyFuture = scheduler.schedule(() -> { + state = ServiceState.READY; + this.notifyFuture = null; + notifyReadyListeners(); + }, STARTUP_DELAY_SECONDS, TimeUnit.SECONDS); + } + if (logger.isTraceEnabled()) { + Matcher matcher = LOG_PATTERN.matcher(line); + if (matcher.matches()) { + String component = matcher.group(2); + String message = matcher.group(3); + logger.trace("{}: {}", component, message); + } else { + logger.trace(line); + } + } + } + } catch (IOException e) { + if (!state.equals(ServiceState.SHUTTING_DOWN)) { + logger.debug("{}", errorMessage, e); + } + } + } + + private void notifyExitListeners(int exitCode) { + for (NodeProcessListener listener : processListeners) { + listener.onNodeExit(exitCode); + } + } + + private void notifyReadyListeners() { + for (NodeProcessListener listener : processListeners) { + listener.onNodeReady(port); + } + } + + private Path extractResourceToTempFile(String resourcePath) throws IOException { + Path tempFile = Files.createTempFile("node-script-", ".js"); + try (InputStream in = getClass().getResourceAsStream(resourcePath)) { + if (in == null) { + throw new IOException("Resource not found: " + resourcePath); + } + Files.copy(in, tempFile, StandardCopyOption.REPLACE_EXISTING); + } + tempFile.toFile().deleteOnExit(); // Ensure the temp file is deleted on JVM exit + return tempFile; + } + + private int findAvailablePort() throws IOException { + ServerSocket serverSocket = null; + try { + serverSocket = new ServerSocket(0); + return serverSocket.getLocalPort(); + } finally { + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException e) { + logger.debug("Failed to close ServerSocket", e); + } + } + } + } + + public interface NodeProcessListener { + void onNodeExit(int exitCode); + + void onNodeReady(int port); + } + + public enum ServiceState { + /** + * The service is up and ready. + */ + READY, + + /** + * The service is in the process of starting but not yet ready. + */ + STARTING, + + /** + * The service is in the process of shutting down, so it shouldn't be restarted. + */ + SHUTTING_DOWN + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/NodeJSRuntimeManager.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/NodeJSRuntimeManager.java new file mode 100644 index 00000000000..73fdad6a1ea --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/NodeJSRuntimeManager.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.util.InputStreamResponseListener; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.openhab.core.OpenHAB; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +/** + * Manages the Node.js runtime for the Matter binding + * + * This class provides methods for checking the system installed version of Node.js, + * downloading and extracting the latest version of Node.js, and finding the Node.js + * executable. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class NodeJSRuntimeManager { + private final Logger logger = LoggerFactory.getLogger(NodeJSRuntimeManager.class); + + private static final String NODE_BASE_VERSION = "v22"; + private static final String NODE_DEFAULT_VERSION = "v22.12.0"; + private static final String NODE_MIN_VERSION = "v18.0.0"; + + private static final String NODE_INDEX_URL = "https://nodejs.org/dist/index.json"; + + private static final String BASE_URL = "https://nodejs.org/dist/"; + private static final String CACHE_DIR = Paths + .get(OpenHAB.getUserDataFolder(), "cache", "org.openhab.binding.matter", "node_cache").toString(); + + private String platform = ""; + private String arch = ""; + private String nodeExecutable = ""; + private org.eclipse.jetty.client.HttpClient client; + + public NodeJSRuntimeManager(HttpClient client) { + this.client = client; + detectPlatformAndArch(); + } + + public String getNodePath() throws IOException { + // Check if system installed node is at least the minimum required version + if (checkSystemInstalledVersion(NODE_MIN_VERSION)) { + logger.debug("Using system installed node"); + return nodeExecutable; + } + + // Download the latest version of Node.js if not already installed + String version = getLatestVersion(); + String cacheDir = CACHE_DIR + File.separator + platform + "-" + arch + File.separator + version; + Path nodePath = findNodeExecutable(cacheDir, version); + + if (nodePath == null) { + downloadAndExtract(cacheDir, version); + nodePath = findNodeExecutable(cacheDir, version); + if (nodePath == null) { + throw new IOException("Unable to locate Node.js executable after download and extraction"); + } + } + + return nodePath.toString(); + } + + private void detectPlatformAndArch() { + String os = Optional.ofNullable(System.getProperty("os.name")).orElseGet(() -> "unknown").toLowerCase(); + String arch = Optional.ofNullable(System.getProperty("os.arch")).orElseGet(() -> "unknown").toLowerCase(); + + if (os.contains("win")) { + platform = "win"; + nodeExecutable = "node.exe"; + } else if (os.contains("mac")) { + platform = "darwin"; + nodeExecutable = "node"; + } else if (os.contains("nux")) { + platform = "linux"; + nodeExecutable = "node"; + } else { + throw new UnsupportedOperationException("Unsupported operating system"); + } + + if (arch.contains("amd64") || arch.contains("x86_64")) { + this.arch = "x64"; + } else if (arch.contains("aarch64") || arch.contains("arm64")) { + this.arch = "arm64"; + } else if (arch.contains("arm")) { + this.arch = "armv7l"; + } else { + throw new UnsupportedOperationException("Unsupported architecture"); + } + } + + private String getLatestVersion() { + try { + ContentResponse response = client.newRequest(NODE_INDEX_URL).method(HttpMethod.GET).send(); + String json = response.getContentAsString(); + Gson gson = new Gson(); + Type listType = new TypeToken>() { + }.getType(); + List versions = gson.fromJson(json, listType); + if (versions != null) { + NodeVersion latest = versions.stream().filter(v -> v.version.startsWith(NODE_BASE_VERSION + ".")) + .max(Comparator.comparing(v -> v.version)).orElse(null); + if (latest != null) { + return latest.version; + } else { + logger.debug("Could not find latest version of Node.js, using default version: {}", + NODE_DEFAULT_VERSION); + } + } + } catch (Exception e) { + logger.debug("Could not fetch latest version of Node.js, using default version: {}", NODE_DEFAULT_VERSION, + e); + } + return NODE_DEFAULT_VERSION; + } + + private @Nullable Path findNodeExecutable(String cacheDir, String version) throws IOException { + Path rootDir = Paths.get(cacheDir); + if (!Files.exists(rootDir)) { + return null; + } + + try (DirectoryStream stream = Files.newDirectoryStream(rootDir)) { + for (Path path : stream) { + if (Files.isDirectory(path) && path.getFileName().toString().startsWith("node-" + version)) { + Path execPath = path.resolve("bin").resolve(nodeExecutable); + if (Files.exists(execPath)) { + return execPath; + } + + // windows does not have a 'bin' directory + execPath = path.resolve(nodeExecutable); + if (Files.exists(execPath)) { + return execPath; + } + } + } + } + return null; + } + + private void downloadAndExtract(String cacheDir, String version) throws IOException { + String fileName = "node-" + version + "-" + platform + "-" + arch + + ("win".equals(platform) ? ".zip" : ".tar.gz"); + String downloadUrl = BASE_URL + version + "/" + fileName; + + Path downloadPath = Paths.get(cacheDir, fileName); + Files.createDirectories(downloadPath.getParent()); + + logger.info("Downloading Node.js from: {}", downloadUrl); + try { + InputStreamResponseListener listener = new InputStreamResponseListener(); + client.newRequest(downloadUrl).method(HttpMethod.GET).send(listener); + Response response = listener.get(5, TimeUnit.SECONDS); + + if (response.getStatus() == HttpStatus.OK_200) { + try (InputStream responseContent = listener.getInputStream()) { + Files.copy(responseContent, downloadPath, StandardCopyOption.REPLACE_EXISTING); + } + } else { + throw new IOException("Failed to download Node.js: HTTP " + response.getStatus()); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Download interrupted", e); + } catch (TimeoutException | ExecutionException e) { + throw new IOException("Failed to download Node.js", e); + } + + logger.debug("Extracting Node.js"); + if ("win".equals(platform)) { + unzip(downloadPath.toString(), cacheDir); + } else { + untar(downloadPath.toString(), cacheDir); + } + + Files.delete(downloadPath); + } + + private void unzip(String zipFilePath, String destDir) throws IOException { + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFilePath))) { + ZipEntry zipEntry; + while ((zipEntry = zis.getNextEntry()) != null) { + Path newPath = Paths.get(destDir, zipEntry.getName()); + if (zipEntry.isDirectory()) { + Files.createDirectories(newPath); + } else { + Files.createDirectories(newPath.getParent()); + Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING); + } + zis.closeEntry(); + } + } + } + + private void untar(String tarFilePath, String destDir) throws IOException { + ProcessBuilder pb = new ProcessBuilder("tar", "-xzf", tarFilePath, "-C", destDir); + Process p = pb.start(); + try { + p.waitFor(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Interrupted while extracting tar file", e); + } + } + + private boolean checkSystemInstalledVersion(String requiredVersion) { + try { + Process process = new ProcessBuilder(nodeExecutable, "--version").start(); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String versionLine = reader.readLine(); + if (versionLine == null || !versionLine.startsWith("v")) { + logger.debug("unexpected node output {}", versionLine); + return false; + } + logger.debug("node found {}", versionLine); + String currentVersion = versionLine.substring(1); // Remove the leading 'v' + return compareVersions(currentVersion, requiredVersion) >= 0; + } + } catch (Exception e) { + return false; + } + } + + private int compareVersions(String version1, String version2) { + if (version1.indexOf("v") == 0) { + version1 = version1.substring(1); + } + if (version2.indexOf("v") == 0) { + version2 = version2.substring(1); + } + String[] parts1 = version1.split("\\."); + String[] parts2 = version2.split("\\."); + + int length = Math.max(parts1.length, parts2.length); + for (int i = 0; i < length; i++) { + int v1 = i < parts1.length ? Integer.parseInt(parts1[i]) : 0; + int v2 = i < parts2.length ? Integer.parseInt(parts2[i]) : 0; + + if (v1 != v2) { + return Integer.compare(v1, v2); + } + } + return 0; + } + + static class NodeVersion { + public String version = ""; + + @Override + public String toString() { + return version; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/NodeStateListener.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/NodeStateListener.java new file mode 100644 index 00000000000..14a990263f0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/NodeStateListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.dto.ws.NodeStateMessage; + +/** + * A listener for node state changes + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface NodeStateListener { + + public void onEvent(NodeStateMessage messasge); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/Endpoint.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/Endpoint.java new file mode 100644 index 00000000000..9c459f2a2a1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/Endpoint.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto; + +import java.util.List; +import java.util.Map; + +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; + +/** + * Represents a Matter endpoint + * + * @author Dan Cunningham - Initial contribution + */ +public class Endpoint { + public Integer number; + public Map clusters; + public List children; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/Node.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/Node.java new file mode 100644 index 00000000000..39f31e1ab0f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/Node.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto; + +import java.math.BigInteger; + +/** + * Represents a Matter node + * + * @author Dan Cunningham - Initial contribution + */ +public class Node { + public BigInteger id; + public Endpoint rootEndpoint; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/PairingCodes.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/PairingCodes.java new file mode 100644 index 00000000000..a1959506e4e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/PairingCodes.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto; + +/** + * Represents the pairing codes for a Matter device + * + * @author Dan Cunningham - Initial contribution + */ +public class PairingCodes { + public String manualPairingCode; + public String qrPairingCode; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/ClusterCommand.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/ClusterCommand.java new file mode 100644 index 00000000000..0346cb647b0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/ClusterCommand.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.cluster; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.Nullable; + +/** + * The {@link ClusterCommand} + * + * @author Dan Cunningham - Initial contribution + */ +public class ClusterCommand { + public String commandName; + public Map args; + + /** + * @param commandName + * @param options + */ + public ClusterCommand(String commandName, Map args) { + super(); + this.commandName = commandName; + this.args = args; + } + + public ClusterCommand(String commandName) { + super(); + this.commandName = commandName; + this.args = Collections.emptyMap(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ClusterCommand other = (ClusterCommand) obj; + if (commandName == null) { + if (other.commandName != null) { + return false; + } + } else if (!commandName.equals(other.commandName)) { + return false; + } + if (args == null) { + return other.args == null; + } + return args.equals(other.args); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((commandName == null) ? 0 : commandName.hashCode()); + result = prime * result + ((args == null) ? 0 : args.hashCode()); + return result; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AccessControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AccessControlCluster.java new file mode 100644 index 00000000000..fc145704b89 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AccessControlCluster.java @@ -0,0 +1,747 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * AccessControl + * + * @author Dan Cunningham - Initial contribution + */ +public class AccessControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x001F; + public static final String CLUSTER_NAME = "AccessControl"; + public static final String CLUSTER_PREFIX = "accessControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ACL = "acl"; + public static final String ATTRIBUTE_EXTENSION = "extension"; + public static final String ATTRIBUTE_SUBJECTS_PER_ACCESS_CONTROL_ENTRY = "subjectsPerAccessControlEntry"; + public static final String ATTRIBUTE_TARGETS_PER_ACCESS_CONTROL_ENTRY = "targetsPerAccessControlEntry"; + public static final String ATTRIBUTE_ACCESS_CONTROL_ENTRIES_PER_FABRIC = "accessControlEntriesPerFabric"; + public static final String ATTRIBUTE_COMMISSIONING_ARL = "commissioningArl"; + public static final String ATTRIBUTE_ARL = "arl"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * An attempt to add an Access Control Entry when no more entries are available shall result in a RESOURCE_EXHAUSTED + * error being reported and the ACL attribute shall NOT have the entry added to it. See access control limits. + * See the AccessControlEntriesPerFabric attribute for the actual value of the number of entries per fabric + * supported by the server. + * Each Access Control Entry codifies a single grant of privilege on this Node, and is used by the Access Control + * Privilege Granting algorithm to determine if a subject has privilege to interact with targets on the Node. + */ + public List acl; // 0 list RW F A + /** + * If present, the Access Control Extensions may be used by Administrators to store arbitrary data related to + * fabric’s Access Control Entries. + * The Access Control Extension list shall support a single extension entry per supported fabric. + */ + public List extension; // 1 list RW F A + /** + * This attribute shall provide the minimum number of Subjects per entry that are supported by this server. + * Since reducing this value over time may invalidate ACL entries already written, this value shall NOT decrease + * across time as software updates occur that could impact this value. If this is a concern for a given + * implementation, it is recommended to only use the minimum value required and avoid reporting a higher value than + * the required minimum. + */ + public Integer subjectsPerAccessControlEntry; // 2 uint16 R V + /** + * This attribute shall provide the minimum number of Targets per entry that are supported by this server. + * Since reducing this value over time may invalidate ACL entries already written, this value shall NOT decrease + * across time as software updates occur that could impact this value. If this is a concern for a given + * implementation, it is recommended to only use the minimum value required and avoid reporting a higher value than + * the required minimum. + */ + public Integer targetsPerAccessControlEntry; // 3 uint16 R V + /** + * This attribute shall provide the minimum number of ACL Entries per fabric that are supported by this server. + * Since reducing this value over time may invalidate ACL entries already written, this value shall NOT decrease + * across time as software updates occur that could impact this value. If this is a concern for a given + * implementation, it is recommended to only use the minimum value required and avoid reporting a higher value than + * the required minimum. + */ + public Integer accessControlEntriesPerFabric; // 4 uint16 R V + /** + * This attribute shall provide the set of CommissioningAccessRestrictionEntryStruct applied during commissioning on + * a managed device. + * When present, the CommissioningARL attribute shall indicate the access restrictions applying during + * commissioning. + * Attempts to access data model elements described by an entry in the CommissioningARL attribute during + * commissioning shall result in an error of ACCESS_RESTRICTED. See Access Control Model for more information about + * the features related to controlling access to a Node’s Endpoint Clusters ("Targets" hereafter) from + * other Nodes. + * See Section 9.10.4.2.1, “Managed Device Feature Usage Restrictions” for limitations on the use of access + * restrictions. + */ + public List commissioningArl; // 5 list R V + /** + * This attribute shall provide the set of AccessRestrictionEntryStruct applied to the associated fabric on a + * managed device. + * When present, the ARL attribute shall indicate the access restrictions applying to the accessing fabric. In + * contrast, the CommissioningARL attribute indicates the accessing restrictions that apply when there is no + * accessing fabric, such as during commissioning. + * The access restrictions are externally added/removed based on the particular relationship the device hosting this + * server has with external entities such as its owner, external service provider, or end-user. + * Attempts to access data model elements described by an entry in the ARL attribute for the accessing fabric shall + * result in an error of ACCESS_RESTRICTED. See Access Control Model for more information about the features related + * to controlling access to a Node’s Endpoint Clusters ("Targets" hereafter) from other Nodes. + * See Section 9.10.4.2.1, “Managed Device Feature Usage Restrictions” for limitations on the use of access + * restrictions. + */ + public List arl; // 6 list R F V + // Structs + + /** + * The cluster shall generate AccessControlEntryChanged events whenever its ACL attribute data is changed by an + * Administrator. + * • Each added entry shall generate an event with ChangeType Added. + * • Each changed entry shall generate an event with ChangeType Changed. + * • Each removed entry shall generate an event with ChangeType Removed. + */ + public class AccessControlEntryChanged { + /** + * The Node ID of the Administrator that made the change, if the change occurred via a CASE session. + * Exactly one of AdminNodeID and AdminPasscodeID shall be set, depending on whether the change occurred via a + * CASE or PASE session; the other shall be null. + */ + public BigInteger adminNodeId; // node-id + /** + * The Passcode ID of the Administrator that made the change, if the change occurred via a PASE session. + * Non-zero values are reserved for future use (see PasscodeId generation in PBKDFParamRequest). + * Exactly one of AdminNodeID and AdminPasscodeID shall be set, depending on whether the change occurred via a + * CASE or PASE session; the other shall be null. + */ + public Integer adminPasscodeId; // uint16 + /** + * The type of change as appropriate. + */ + public ChangeTypeEnum changeType; // ChangeTypeEnum + /** + * The latest value of the changed entry. + * This field SHOULD be set if resources are adequate for it; otherwise it shall be set to NULL if resources are + * scarce. + */ + public AccessControlEntryStruct latestValue; // AccessControlEntryStruct + public Integer fabricIndex; // FabricIndex + + public AccessControlEntryChanged(BigInteger adminNodeId, Integer adminPasscodeId, ChangeTypeEnum changeType, + AccessControlEntryStruct latestValue, Integer fabricIndex) { + this.adminNodeId = adminNodeId; + this.adminPasscodeId = adminPasscodeId; + this.changeType = changeType; + this.latestValue = latestValue; + this.fabricIndex = fabricIndex; + } + } + + /** + * The cluster shall generate AccessControlExtensionChanged events whenever its extension attribute data is changed + * by an Administrator. + * • Each added extension shall generate an event with ChangeType Added. + * • Each changed extension shall generate an event with ChangeType Changed. + * • Each removed extension shall generate an event with ChangeType Removed. + */ + public class AccessControlExtensionChanged { + /** + * The Node ID of the Administrator that made the change, if the change occurred via a CASE session. + * Exactly one of AdminNodeID and AdminPasscodeID shall be set, depending on whether the change occurred via a + * CASE or PASE session; the other shall be null. + */ + public BigInteger adminNodeId; // node-id + /** + * The Passcode ID of the Administrator that made the change, if the change occurred via a PASE session. + * Non-zero values are reserved for future use (see PasscodeId generation in PBKDFParamRequest). + * Exactly one of AdminNodeID and AdminPasscodeID shall be set, depending on whether the change occurred via a + * CASE or PASE session; the other shall be null. + */ + public Integer adminPasscodeId; // uint16 + /** + * The type of change as appropriate. + */ + public ChangeTypeEnum changeType; // ChangeTypeEnum + /** + * The latest value of the changed extension. + * This field SHOULD be set if resources are adequate for it; otherwise it shall be set to NULL if resources are + * scarce. + */ + public AccessControlExtensionStruct latestValue; // AccessControlExtensionStruct + public Integer fabricIndex; // FabricIndex + + public AccessControlExtensionChanged(BigInteger adminNodeId, Integer adminPasscodeId, ChangeTypeEnum changeType, + AccessControlExtensionStruct latestValue, Integer fabricIndex) { + this.adminNodeId = adminNodeId; + this.adminPasscodeId = adminPasscodeId; + this.changeType = changeType; + this.latestValue = latestValue; + this.fabricIndex = fabricIndex; + } + } + + /** + * The cluster shall generate a FabricRestrictionReviewUpdate event to indicate completion of a fabric restriction + * review. Due to the requirement to generate this event within a bound time frame of successful receipt of the + * ReviewFabricRestrictions command, this event may include additional steps that the client may present to the user + * in order to help the user locate the user interface for the Managed Device feature. + */ + public class FabricRestrictionReviewUpdate { + /** + * This field shall indicate the Token that can be used to correlate a ReviewFabricRestrictionsResponse with a + * FabricRestrictionReviewUpdate event. + */ + public BigInteger token; // uint64 + /** + * This field shall provide human readable text that may be displayed to the user to help them locate the user + * interface for managing access restrictions for each fabric. + * A device SHOULD implement the Localization Configuration Cluster when it has no other means to determine the + * locale to use for this text. + * Examples include "Please try again and immediately access device display for further instructions." + * or "Please check email associated with your Acme account." + */ + public String instruction; // string + /** + * This field shall indicate the URL for the service associated with the device maker which the user can visit + * to manage fabric limitations. The syntax of this field shall follow the syntax as specified in RFC 1738 and + * shall use the https scheme for internet-hosted URLs. + * • The URL may embed the token, fabric index, fabric vendor, or other information transparently in order to + * pass context about the originating ReviewFabricRestrictions command to the service associated with the URL. + * The service associated with the device vendor may perform vendor ID verification on the fabric from which the + * ReviewFabricRestrictions command originated. + * • If the device grants the request, the ARL attribute in the Access Control Cluster shall be updated to + * reflect the new access rights and a successful response shall be returned to the device making the request + * using the MTaer field of the callbackUrl. If the request is denied, the ARL attribute shall remain unchanged + * and a failure response shall be returned to the device making the request using the MTaer field of the + * callbackUrl. + * • The device using this mechanism shall provide a service at the URL that can accept requests for additional + * access and return responses indicating whether the requests were granted or denied. + * • This URL will typically lead to a server which (e.g. by looking at the User-Agent) redirects the user to + * allow viewing, downloading, installing or using a manufacturer-provided means for guiding the user through + * the process to review and approve or deny the request. The device manufacturer may choose to use a + * constructed URL which is valid in a HTTP GET request (i.e. dedicated for the product) such as, for example, + * https://domain.example/arl-app?vid=FFF1& pid=1234. If a client follows or launches the + * ARLRequestFlowUrl, it shall expand it as described in Section 9.10.9.3.4, “ARLRequestFlowUrl format”. + * • A manufacturer contemplating using this flow should realize that + * ◦ This flow typically requires internet access to access the URL, and access extension may fail when internet + * connectivity is not available. + * ◦ If the flow prefers to redirect the user to an app which is available on popular platforms, it SHOULD also + * provide a fallback option such as a web browser interface to ensure users can complete access extension. + * ### ARLRequestFlowUrl format + * The ARLRequestFlowUrl shall contain a query component (see RFC 3986 section 3.4) composed of one or more + * key-value pairs: + * • The query shall use the & delimiter between key/value pairs. + * • The key-value pairs shall in the format name=<value> where name is the key name, and + * <value> is the contents of the value encoded with proper URL-encoded escaping. + * • If key MTcu is present, it shall have a value of "_" (i.e. MTcu=_). This is the + * "callback URL (CallbackUrl) placeholder". + * • Any key whose name begins with MT not mentioned in the previous bullets shall be reserved for future use by + * this specification. Manufacturers shall NOT include query keys starting with MT in the ARLRequestFlowUrl + * unless they are referenced by a version of this specification. + * Any other element in the ARLRequestFlowUrl query field not covered by the above rules, as well as the + * fragment field (if present), shall remain including the order of query key/value pairs present. + * Once the URL is obtained, it shall be expanded to form a final URL (ExpandedARLRequestFlowUrl) by proceeding + * with the following substitution algorithm on the original ARLRequestFlowUrl: + * 1. If key MTcu is present, compute the CallbackUrl desired (see Section 9.10.9.3.5, “CallbackUrl format for + * ARL Request Flow response”), and substitute the placeholder value "_" (i.e. in MTcu=_) in the + * ARLRequestFlowUrl with the desired contents, encoded with proper URL-encoded escaping (see RFC 3986 section + * 2). + * The final URL after expansion (ExpandedARLRequestFlowUrl) shall be the one to follow, rather than the + * original value obtained from the FabricRestrictionReviewUpdate event. + * ### CallbackUrl format for ARL Request Flow response + * If a CallbackUrl field (i.e. MTcu=) query field placeholder is present in the ARLRequestFlowUrl, the + * client may replace the placeholder value "_" in the ExpandedARLRequestFlowUrl with a URL that the + * manufacturer flow can use to make a smooth return to the client when the ARL flow has terminated. + * This URL field may contain a query component (see RFC 3986 section 3.4). If a query is present, it shall be + * composed of one or more key-value pairs: + * • The query shall use the & delimiter between key/value pairs. + * • The key-value pairs shall follow the format name=<value> where name is the key name, and + * <value> is the contents of the value encoded with proper URL-encoded escaping. + * • If key MTaer is present, it shall have a value of "_" (i.e. MTaer=_). This is the + * placeholder for a "access extension response" provided by the manufacturer flow to the client. The + * manufacturer flow shall replace this placeholder with the final status of the access extension request, which + * shall be formatted following Expansion of CallbackUrl by the manufacturer custom flow and encoded with proper + * URL-encoded escaping. + * • Any key whose name begins with MT not mentioned in the previous bullets shall be reserved for future use by + * this specification. + * Any other element in the CallbackUrl query field not covered by the above rules, as well as the fragment + * field (if present), shall remain as provided by the client through embedding within the + * ExpandedARLRequestFlowUrl, including the order of query key/value pairs present. + * Once the CallbackUrl is obtained by the manufacturer flow, it may be expanded to form a final + * ExpandedARLRequestCallbackUrl URL to be used by proceeding with the following substitution algorithm on the + * provided CallbackUrl: + * • If key MTaer is present, the manufacturer custom flow having received the initial query containing the + * CallbackUrl shall substitute the placeholder value "_" (i.e. in MTaer=_) in the CallbackUrl + * with the final status of the access extension request flow which shall be one of the following. Any value + * returned in the MTaer field not listed above shall be considered an error and shall be treated as + * GeneralFailure. + * ◦ Success - The flow completed successfully and the ARL attribute was updated. The client may now read the + * ARL attribute to determine the new access restrictions. + * ◦ NoChange - The ARL attribute was already listing minimum restrictions for the requesting fabric. + * ◦ GeneralFailure - The flow failed for an unspecified reason. + * ◦ FlowAuthFailure - The user failed to authenticate to the flow. + * ◦ NotFound - Access extension failed because the target fabric was not found. + * A manufacturer custom flow having received an ExpandedARLRequestFlowUrl SHOULD attempt to open the + * ExpandedARLRequestCallbackUrl, on completion of the request, if an ExpandedARLRequestCallbackUrl was computed + * from the CallbackUrl and opening such a URL is supported. + * ### Examples of ARLRequestFlowUrl URLs + * Below are some examples of valid ExpandedARLRequestFlowUrl for several valid values of ARLRequestFlowUrl, as + * well as some examples of invalid values of ARLRequestFlowUrl: + * • Invalid URL with no query string: http scheme is not allowed: + * ◦ http://company.domain.example/matter/arl/vFFF1p1234 + * • Valid URL : + * ◦ https://company.domain.example/matter/arl/vFFF1p1234 + * • Valid URL, CallbackUrl requested: + * ◦ Before expansion: + * https://company.domain.example/matter/arl?vid=FFF1&pid=1234&MTcu=_ + * ◦ After expansion: + * https://company.domain.example/matter/arl?vid=FFF1&pid=1234&MTcu=https%3A%2F%2Fc + * lient.domain.example%2Fcb%3Ftoken%3DmAsJ6_vqbr-vjDiG_w%253D%253D%26MTaer%3D_ + * ◦ The ExpandedARLRequestFlowUrl URL contains: + * ▪ A CallbackUrl with a client-provided arbitrary token= key/value pair and the MTaer= key/value + * pair place-holder to indicate support for a return access extension completion status: + * https://client.domain.example/cb?token=mAsJ6_vqbr-vjDiG_w%3D%3D&MTaer=_ + * ▪ After expansion of the CallbackUrl (MTcu key) into an ExpandedCallbackUrl, with an example return access + * extension completion status of Success, the ExpandedARLRequestCallbackUrl would be: + * https://client.domain.example/cb?token=mAsJ6_vqbr-vjDiG_w%3D%3D&MTaer=Success + * Note that the MTcu key/value pair was initially provided URL-encoded within the ExpandedARLRequestFlowUrl URL + * and the MTaer=_ key/value pair placeholder now contains a substituted returned completion status. + * • Invalid URL, due to MTza=79 key/value pair in reserved MT-prefixed keys reserved for future use: + * ◦ https://company.domain.example/matter/arl?vid=FFF1&pid=1234&MTop=_&MTza=79 + */ + public String arlRequestFlowUrl; // string + public Integer fabricIndex; // FabricIndex + + public FabricRestrictionReviewUpdate(BigInteger token, String instruction, String arlRequestFlowUrl, + Integer fabricIndex) { + this.token = token; + this.instruction = instruction; + this.arlRequestFlowUrl = arlRequestFlowUrl; + this.fabricIndex = fabricIndex; + } + } + + public class AccessControlTargetStruct { + public Integer cluster; // cluster-id + public Integer endpoint; // endpoint-no + public Integer deviceType; // devtype-id + + public AccessControlTargetStruct(Integer cluster, Integer endpoint, Integer deviceType) { + this.cluster = cluster; + this.endpoint = endpoint; + this.deviceType = deviceType; + } + } + + public class AccessControlEntryStruct { + /** + * The privilege field shall specify the level of privilege granted by this Access Control Entry. + * Each privilege builds upon its predecessor, expanding the set of actions that can be performed upon a Node. + * Administer is the highest privilege, and is special as it pertains to the administration of privileges + * itself, via the Access Control Cluster. + * When a Node is granted a particular privilege, it is also implicitly granted all logically lower privilege + * levels as well. The following diagram illustrates how the higher privilege levels subsume the lower privilege + * levels: + * Individual clusters shall define whether attributes are readable, writable, or both readable and writable. + * Clusters also shall define which privilege is minimally required to be able to perform a particular read or + * write action on those attributes, or invoke particular commands. Device type specifications may further + * restrict the privilege required. + * The Access Control Cluster shall require the Administer privilege to observe and modify the Access Control + * Cluster itself. The Administer privilege shall NOT be used on Access Control Entries which use the Group auth + * mode. + */ + public AccessControlEntryPrivilegeEnum privilege; // AccessControlEntryPrivilegeEnum + /** + * The AuthMode field shall specify the authentication mode required by this Access Control Entry. + */ + public AccessControlEntryAuthModeEnum authMode; // AccessControlEntryAuthModeEnum + /** + * The subjects field shall specify a list of Subject IDs, to which this Access Control Entry grants access. + * Device types may impose additional constraints on the minimum number of subjects per Access Control Entry. + * An attempt to create an entry with more subjects than the node can support shall result in a + * RESOURCE_EXHAUSTED error and the entry shall NOT be created. + * ### Subject ID shall be of type uint64 with semantics depending on the entry’s AuthMode as follows: + * An empty subjects list indicates a wildcard; that is, this entry shall grant access to any Node that + * successfully authenticates via AuthMode. The subjects list shall NOT be empty if the entry’s AuthMode is + * PASE. + * The PASE AuthMode is reserved for future use (see Section 6.6.2.9, “Bootstrapping of the Access Control + * Cluster”). An attempt to write an entry with AuthMode set to PASE shall fail with a status code of + * CONSTRAINT_ERROR. + * For PASE authentication, the Passcode ID identifies the required passcode verifier, and shall be 0 for the + * default commissioning passcode. + * For CASE authentication, the Subject ID is a distinguished name within the Operational Certificate shared + * during CASE session establishment, the type of which is determined by its range to be one of: + * • a Node ID, which identifies the required source node directly (by ID) + * • a CASE Authenticated Tag, which identifies the required source node indirectly (by tag) + * For Group authentication, the Group ID identifies the required group, as defined in the Group Key Management + * Cluster. + */ + public List subjects; // list + /** + * The targets field shall specify a list of AccessControlTargetStruct, which define the clusters on this Node + * to which this Access Control Entry grants access. + * Device types may impose additional constraints on the minimum number of targets per Access Control Entry. + * An attempt to create an entry with more targets than the node can support shall result in a + * RESOURCE_EXHAUSTED error and the entry shall NOT be created. + * A single target shall contain at least one field (Cluster, Endpoint, or DeviceType), and shall NOT contain + * both an Endpoint field and a DeviceType field. + * A target grants access based on the presence of fields as follows: + * An empty targets list indicates a wildcard: that is, this entry shall grant access to all cluster instances + * on all endpoints on this Node. + */ + public List targets; // list + public Integer fabricIndex; // FabricIndex + + public AccessControlEntryStruct(AccessControlEntryPrivilegeEnum privilege, + AccessControlEntryAuthModeEnum authMode, List subjects, + List targets, Integer fabricIndex) { + this.privilege = privilege; + this.authMode = authMode; + this.subjects = subjects; + this.targets = targets; + this.fabricIndex = fabricIndex; + } + } + + public class AccessControlExtensionStruct { + /** + * This field may be used by manufacturers to store arbitrary TLV-encoded data related to a fabric’s Access + * Control Entries. + * The contents shall consist of a top-level anonymous list; each list element shall include a profile-specific + * tag encoded in fully-qualified form. + * Administrators may iterate over this list of elements, and interpret selected elements at their discretion. + * The content of each element is not specified, but may be coordinated among manufacturers at their discretion. + */ + public OctetString data; // octstr + public Integer fabricIndex; // FabricIndex + + public AccessControlExtensionStruct(OctetString data, Integer fabricIndex) { + this.data = data; + this.fabricIndex = fabricIndex; + } + } + + /** + * This structure describes an access restriction that would be applied to a specific data model element on a given + * endpoint/cluster pair (see AccessRestrictionEntryStruct). + */ + public class AccessRestrictionStruct { + /** + * This field shall indicate the type of restriction, for example, AttributeAccessForbidden. + */ + public AccessRestrictionTypeEnum type; // AccessRestrictionTypeEnum + /** + * This field shall indicate the element Manufacturer Extensible Identifier (MEI) associated with the element + * type subject to the access restriction, based upon the AccessRestrictionTypeEnum. When the Type is + * AttributeAccessForbidden or AttributeWriteForbidden, this value shall be considered of type attrib-id (i.e. + * an attribute identifier). When the Type is CommandForbidden, this value shall be considered of type + * command-id (i.e. an attribute identifier). When the Type is EventForbidden, this value shall be considered of + * type event-id (i.e. an event identifier). + * A null value shall indicate the wildcard value for the given value of Type (i.e. all elements associated with + * the Type under the associated endpoint and cluster for the containing AccessRestrictionEntryStruct). + */ + public Integer id; // uint32 + + public AccessRestrictionStruct(AccessRestrictionTypeEnum type, Integer id) { + this.type = type; + this.id = id; + } + } + + /** + * This structure describes a current access restriction on the fabric. + */ + public class AccessRestrictionEntryStruct { + /** + * This field shall indicate the endpoint having associated access restrictions scoped to the associated fabric + * of the list containing the entry. + */ + public Integer endpoint; // endpoint-no + /** + * This field shall indicate the cluster having associated access restrictions under the entry’s Endpoint, + * scoped to the associated fabric of the list containing the entry. + */ + public Integer cluster; // cluster-id + /** + * This field shall indicate the set of restrictions applying to the Cluster under the given Endpoint, scoped to + * the associated fabric of the list containing the entry. + * This list shall NOT be empty. + */ + public List restrictions; // list + public Integer fabricIndex; // FabricIndex + + public AccessRestrictionEntryStruct(Integer endpoint, Integer cluster, + List restrictions, Integer fabricIndex) { + this.endpoint = endpoint; + this.cluster = cluster; + this.restrictions = restrictions; + this.fabricIndex = fabricIndex; + } + } + + /** + * This structure describes a current access restriction when there is no accessing fabric. + */ + public class CommissioningAccessRestrictionEntryStruct { + /** + * This field shall indicate the endpoint having associated access restrictions scoped to the associated fabric + * of the list containing the entry. + */ + public Integer endpoint; // endpoint-no + /** + * This field shall indicate the cluster having associated access restrictions under the entry’s Endpoint, + * scoped to the associated fabric of the list containing the entry. + */ + public Integer cluster; // cluster-id + /** + * This field shall indicate the set of restrictions applying to the Cluster under the given Endpoint, scoped to + * the associated fabric of the list containing the entry. + * This list shall NOT be empty. + */ + public List restrictions; // list + + public CommissioningAccessRestrictionEntryStruct(Integer endpoint, Integer cluster, + List restrictions) { + this.endpoint = endpoint; + this.cluster = cluster; + this.restrictions = restrictions; + } + } + + // Enums + public enum ChangeTypeEnum implements MatterEnum { + CHANGED(0, "Changed"), + ADDED(1, "Added"), + REMOVED(2, "Removed"); + + public final Integer value; + public final String label; + + private ChangeTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * ### Proxy View Value + * ### This value implicitly grants View privileges + */ + public enum AccessControlEntryPrivilegeEnum implements MatterEnum { + VIEW(1, "View"), + PROXY_VIEW(2, "Proxy View"), + OPERATE(3, "Operate"), + MANAGE(4, "Manage"), + ADMINISTER(5, "Administer"); + + public final Integer value; + public final String label; + + private AccessControlEntryPrivilegeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum AccessRestrictionTypeEnum implements MatterEnum { + ATTRIBUTE_ACCESS_FORBIDDEN(0, "Attribute Access Forbidden"), + ATTRIBUTE_WRITE_FORBIDDEN(1, "Attribute Write Forbidden"), + COMMAND_FORBIDDEN(2, "Command Forbidden"), + EVENT_FORBIDDEN(3, "Event Forbidden"); + + public final Integer value; + public final String label; + + private AccessRestrictionTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum AccessControlEntryAuthModeEnum implements MatterEnum { + PASE(1, "Pase"), + CASE(2, "Case"), + GROUP(3, "Group"); + + public final Integer value; + public final String label; + + private AccessControlEntryAuthModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature indicates the device supports ACL Extension attribute. + */ + public boolean extension; + /** + * + * This feature is for a device that is managed by a service associated with the device vendor and which imposes + * default access restrictions upon each new fabric added to it. This could arise, for example, if the device is + * managed by a service provider under contract to an end-user, in such a way that the manager of the device + * does not unconditionally grant universal access to all of a device’s functionality, even for fabric + * administrators. For example, many Home Routers are managed by an Internet Service Provider (a service), and + * these services often have a policy that requires them to obtain user consent before certain administrative + * functions can be delegated to a third party (e.g., a fabric Administrator). These restrictions are expressed + * using an Access Restriction List (ARL). + * The purpose of this feature on the Access Control cluster is to indicate to a fabric Administrator that + * access by it to specific attributes, commands and/or events for specific clusters is currently prohibited. + * Attempts to access these restricted data model elements shall result in an error of ACCESS_RESTRICTED. + * A device that implements this feature shall have a mechanism to honor the ReviewFabricRestrictions command, + * such as user interfaces or service interactions associated with a service provider or the device + * manufacturer, which allows the owner (or subscriber) to manage access restrictions for each fabric. The user + * interface design, which includes the way restrictions are organized and presented to the user, is not + * specified, but SHOULD be usable by non-expert end-users from common mobile devices, personal computers, or an + * on-device user interface. + * Controllers and clients SHOULD incorporate generic handling of the ACCESS_RESTRICTED error code, when it + * appears in allowed contexts, in order to gracefully handle situations where this feature is encountered. + * Device vendors that adopt this feature SHOULD be judicious in its use given the risk of unexpected behavior + * in controllers and clients. + * For certification testing, a device that implements this feature shall provide a way for all restrictions to + * be removed. + * The ARL attribute provides the set of restrictions currently applied to this fabric. + * The ReviewFabricRestrictions command provides a way for the fabric Administrator to request that the server + * triggers a review of the current fabric restrictions, by involving external entities such as end-users, or + * other services associated with the manager of the device hosting the server. This review process may involve + * communication between external services and the user, and may take an unpredictable amount of time to + * complete since an end-user may need to visit some resources, such as a mobile application or web site. A + * FabricRestrictionReviewUpdate event will be generated by the device within a predictable time period of the + * ReviewFabricRestrictionsResponse (see ReviewFabricRestrictions for specification of this time period), and + * this event can be correlated with the ReviewFabricRestrictionsResponse using a token provided in both. The + * device may provide instructions or a Redirect URL in the FabricRestrictionReviewUpdate event in order to help + * the user access the features required for managing per-fabric restrictions. + * See Section 6.6.2, “Model” for a description of how access control is impacted by the ARL attribute. + * ### Managed Device Feature Usage Restrictions + * Use of this feature shall be limited to the mandatory clusters of endpoints having a device type that + * explicitly permits its use in the Device Library Specification. As a reminder, the device types associated + * with an endpoint are listed in the Descriptor cluster of the endpoint. + * In addition, use of this feature shall NOT restrict the following clusters on any endpoint: + * 1. the Descriptor Cluster (0x001D) + * 2. the Binding Cluster (0x001E) + * 3. the Network Commissioning Cluster (0x0031) + * 4. the Identify Cluster (0x0003) + * 5. the Groups Cluster (0x0004) + * In addition, use of this feature shall NOT restrict the global attributes of any cluster. + * Because ARLs cannot be used to restrict root node access or access to any clusters required for + * commissioning, administrators may determine the current restrictions of the ARL at any point, including + * during commissioning after joining the fabric. + */ + public boolean managedDevice; + + public FeatureMap(boolean extension, boolean managedDevice) { + this.extension = extension; + this.managedDevice = managedDevice; + } + } + + public AccessControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 31, "AccessControl"); + } + + protected AccessControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command signals to the service associated with the device vendor that the fabric administrator would like a + * review of the current restrictions on the accessing fabric. This command includes an optional list of ARL entries + * that the fabric administrator would like removed. + * In response, a ReviewFabricRestrictionsResponse is sent which contains a token that can be used to correlate a + * review request with a FabricRestrictionReviewUpdate event. + * Within 1 hour of the ReviewFabricRestrictionsResponse, the FabricRestrictionReviewUpdate event shall be + * generated, in order to indicate completion of the review and any additional steps required by the user for the + * review. + * A review may include obtaining consent from the user, which can take time. For example, the user may need to + * respond to an email or a push notification. + * The ARL attribute may change at any time due to actions taken by the user, or the service associated with the + * device vendor. + */ + public static ClusterCommand reviewFabricRestrictions(List arl) { + Map map = new LinkedHashMap<>(); + if (arl != null) { + map.put("arl", arl); + } + return new ClusterCommand("reviewFabricRestrictions", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "acl : " + acl + "\n"; + str += "extension : " + extension + "\n"; + str += "subjectsPerAccessControlEntry : " + subjectsPerAccessControlEntry + "\n"; + str += "targetsPerAccessControlEntry : " + targetsPerAccessControlEntry + "\n"; + str += "accessControlEntriesPerFabric : " + accessControlEntriesPerFabric + "\n"; + str += "commissioningArl : " + commissioningArl + "\n"; + str += "arl : " + arl + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AccountLoginCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AccountLoginCluster.java new file mode 100644 index 00000000000..d8829cfbf1d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AccountLoginCluster.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * AccountLogin + * + * @author Dan Cunningham - Initial contribution + */ +public class AccountLoginCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x050E; + public static final String CLUSTER_NAME = "AccountLogin"; + public static final String CLUSTER_PREFIX = "accountLogin"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + // Structs + + /** + * This event can be used by the Content App to indicate that the current user has logged out. In response to this + * event, the Fabric Admin shall remove access to this Content App by the specified Node. If no Node is provided, + * then the Fabric Admin shall remove access to all non-Admin Nodes. + */ + public class LoggedOut { + /** + * This field shall provide the Node ID corresponding to the user account that has logged out, if that Node ID + * is available. If it is NOT available, this field shall NOT be present in the event. + */ + public BigInteger node; // node-id + + public LoggedOut(BigInteger node) { + this.node = node; + } + } + + public AccountLoginCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1294, "AccountLogin"); + } + + protected AccountLoginCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * The purpose of this command is to determine if the active user account of the given Content App matches the + * active user account of a given Commissionee, and when it does, return a Setup PIN code which can be used for + * password-authenticated session establishment (PASE) with the Commissionee. + * For example, a Video Player with a Content App Platform may invoke this command on one of its Content App + * endpoints to facilitate commissioning of a Phone App made by the same vendor as the Content App. If the accounts + * match, then the Content App may return a setup code that can be used by the Video Player to commission the Phone + * App without requiring the user to physically input a setup code. + * The account match is determined by the Content App using a method which is outside the scope of this + * specification and will typically involve a central service which is in communication with both the Content App + * and the Commissionee. The GetSetupPIN command is needed in order to provide the Commissioner/Admin with a Setup + * PIN when this Commissioner/Admin is operated by a different vendor from the Content App. + * This method is used to facilitate Setup PIN exchange (for PASE) between Commissioner and Commissionee when the + * same user account is active on both nodes. With this method, the Content App satisfies proof of possession + * related to commissioning by requiring the same user account to be active on both Commissionee and Content App, + * while the Commissioner/Admin ensures user consent by prompting the user prior to invocation of the command. + * Upon receipt of this command, the Content App checks if the account associated with the Temporary Account + * Identifier sent by the client is the same account that is active on itself. If the accounts are the same, then + * the Content App returns the GetSetupPIN Response which includes a Setup PIN that may be used for PASE with the + * Commissionee. + * The Temporary Account Identifier for a Commissionee may be populated with the Rotating ID field of the client’s + * commissionable node advertisement (see Rotating Device Identifier section in [MatterCore]) encoded as an octet + * string where the octets of the Rotating Device Identifier are encoded as 2-character sequences by representing + * each octet’s value as a 2-digit hexadecimal number, using uppercase letters. + * The Setup PIN is a character string so that it can accommodate different future formats, including alpha-numeric + * encodings. For a Commissionee it shall be populated with the Manual Pairing Code (see Manual Pairing Code section + * in [MatterCore]) encoded as a string (11 characters) or the Passcode portion of the Manual Pairing Code (when + * less than 11 characters) . + * The server shall implement rate limiting to prevent brute force attacks. No more than 10 unique requests in a 10 + * minute period shall be allowed; a command response status of FAILURE should sent for additional commands received + * within the 10 minute period. Because access to this command is limited to nodes with Admin-level access, and the + * user is prompted for consent prior to Commissioning, there are in place multiple obstacles to successfully + * mounting a brute force attack. A Content App that supports this command shall ensure that the Temporary Account + * Identifier used by its clients is not valid for more than 10 minutes. + */ + public static ClusterCommand getSetupPin(String tempAccountIdentifier) { + Map map = new LinkedHashMap<>(); + if (tempAccountIdentifier != null) { + map.put("tempAccountIdentifier", tempAccountIdentifier); + } + return new ClusterCommand("getSetupPin", map); + } + + /** + * The purpose of this command is to allow the Content App to assume the user account of a given Commissionee by + * leveraging the Setup PIN code input by the user during the commissioning process. + * For example, a Video Player with a Content App Platform may invoke this command on one of its Content App + * endpoints after the commissioning has completed of a Phone App made by the same vendor as the Content App. The + * Content App may determine whether the Temporary Account Identifier maps to an account with a corresponding Setup + * PIN and, if so, it may automatically login to the account for the corresponding user. The end result is that a + * user performs commissioning of a Phone App to a Video Player by inputting the Setup PIN for the Phone App into + * the Video Player UX. Once commissioning has completed, the Video Player invokes this command to allow the + * corresponding Content App to assume the same user account as the Phone App. + * The verification of Setup PIN for the given Temporary Account Identifier is determined by the Content App using a + * method which is outside the scope of this specification and will typically involve a central service which is in + * communication with both the Content App and the Commissionee. Implementations of such a service should impose + * aggressive time outs for any mapping of Temporary Account Identifier to Setup PIN in order to prevent accidental + * login due to delayed invocation. + * Upon receipt, the Content App checks if the account associated with the client’s Temp Account Identifier has a + * current active Setup PIN with the given value. If the Setup PIN is valid for the user account associated with the + * Temp Account Identifier, then the Content App may make that user account active. + * The Temporary Account Identifier for a Commissionee may be populated with the Rotating ID field of the client’s + * commissionable node advertisement encoded as an octet string where the octets of the Rotating Device Identifier + * are encoded as 2-character sequences by representing each octet’s value as a 2-digit hexadecimal number, using + * uppercase letters. + * The Setup PIN for a Commissionee may be populated with the Manual Pairing Code encoded as a string of decimal + * numbers (11 characters) or the Passcode portion of the Manual Pairing Code encoded as a string of decimal numbers + * (8 characters) . + * The server shall implement rate limiting to prevent brute force attacks. No more than 10 unique requests in a 10 + * minute period shall be allowed; a command response status of FAILURE should sent for additional commands received + * within the 10 minute period. Because access to this command is limited to nodes with Admin-level access, and the + * user is involved when obtaining the SetupPIN, there are in place multiple obstacles to successfully mounting a + * brute force attack. A Content App that supports this command shall ensure that the Temporary Account Identifier + * used by its clients is not valid for more than 10 minutes. + */ + public static ClusterCommand login(String tempAccountIdentifier, String setupPin, BigInteger node) { + Map map = new LinkedHashMap<>(); + if (tempAccountIdentifier != null) { + map.put("tempAccountIdentifier", tempAccountIdentifier); + } + if (setupPin != null) { + map.put("setupPin", setupPin); + } + if (node != null) { + map.put("node", node); + } + return new ClusterCommand("login", map); + } + + /** + * The purpose of this command is to instruct the Content App to clear the current user account. This command SHOULD + * be used by clients of a Content App to indicate the end of a user session. + */ + public static ClusterCommand logout(BigInteger node) { + Map map = new LinkedHashMap<>(); + if (node != null) { + map.put("node", node); + } + return new ClusterCommand("logout", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ActionsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ActionsCluster.java new file mode 100644 index 00000000000..8bda385259c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ActionsCluster.java @@ -0,0 +1,614 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * Actions + * + * @author Dan Cunningham - Initial contribution + */ +public class ActionsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0025; + public static final String CLUSTER_NAME = "Actions"; + public static final String CLUSTER_PREFIX = "actions"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_ACTION_LIST = "actionList"; + public static final String ATTRIBUTE_ENDPOINT_LISTS = "endpointLists"; + public static final String ATTRIBUTE_SETUP_URL = "setupUrl"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * The ActionList attribute holds the list of actions. Each entry shall have an unique ActionID, and its + * EndpointListID shall exist in the EndpointLists attribute. + */ + public List actionList; // 0 list R V + /** + * The EndpointLists attribute holds the list of endpoint lists. Each entry shall have an unique EndpointListID. + */ + public List endpointLists; // 1 list R V + /** + * The SetupURL attribute (when provided) shall indicate a URL; its syntax shall follow the syntax as specified in + * RFC 1738, max. 512 ASCII characters and shall use the https scheme. The location referenced by this URL shall + * provide additional information for the actions provided: + * • When used without suffix, it shall provide information about the various actions which the cluster provides. + * ◦ Example: SetupURL could take the value of example://Actions or https://domain.example/ Matter/bridgev1/Actions + * for this generic case (access generic info how to use actions provided by this cluster). + * • When used with a suffix of "/?a=" and the decimal value of ActionID for one of the actions, it + * may provide information about that particular action. This could be a deeplink to manufacturer-app/website + * (associated somehow to the server node) with the information/edit-screen for this action so that the user can + * view and update details of the action, e.g. edit the scene, or change the wake-up experience time period. + * ◦ Example of SetupURL with suffix added: example://Actions/?a=12345 or + * https://domain.example/Matter/bridgev1/Actions/?a=12345 for linking to specific info/editing of the action + * with ActionID 0x3039. + */ + public String setupUrl; // 2 string R V + // Structs + + /** + * This event shall be generated when there is a change in the State of an ActionID during the execution of an + * action and the most recent command using that ActionID used an InvokeID data field. + * It provides feedback to the client about the progress of the action. + * Example: When InstantActionWithTransition is invoked (with an InvokeID data field), two StateChanged events will + * be generated: + * • one when the transition starts (NewState=Active) + * • one when the transition completed (NewState=Inactive) + */ + public class StateChanged { + /** + * This field shall be set to the ActionID of the action which has changed state. + */ + public Integer actionId; // uint16 + /** + * This field shall be set to the InvokeID which was provided to the most recent command referencing this + * ActionID. + */ + public Integer invokeId; // uint32 + /** + * This field shall be set to state that the action has changed to. + */ + public ActionStateEnum newState; // ActionStateEnum + + public StateChanged(Integer actionId, Integer invokeId, ActionStateEnum newState) { + this.actionId = actionId; + this.invokeId = invokeId; + this.newState = newState; + } + } + + /** + * This event shall be generated when there is some error which prevents the action from its normal planned + * execution and the most recent command using that ActionID used an InvokeID data field. + * It provides feedback to the client about the non-successful progress of the action. + * Example: When InstantActionWithTransition is invoked (with an InvokeID data field), and another controller + * changes the state of one or more of the involved endpoints during the transition, thus interrupting the + * transition triggered by the action, two events would be generated: + * • StateChanged when the transition starts (NewState=Active) + * • ActionFailed when the interrupting command occurs (NewState=Inactive, Error=interrupted) + * Example: When InstantActionWithTransition is invoked (with an InvokeID data field = 1), and the same client + * invokes an InstantAction with (the same or another ActionId and) InvokeID = 2, and this second command + * interrupts the transition triggered by the first command, these events would be generated: + * • StateChanged (InvokeID=1, NewState=Active) when the transition starts + * • ActionFailed (InvokeID=2, NewState=Inactive, Error=interrupted) when the second command + * interrupts the transition + * • StateChanged (InvokeID=2, NewState=Inactive) upon the execution of the action for the second command + */ + public class ActionFailed { + /** + * This field shall be set to the ActionID of the action which encountered an error. + */ + public Integer actionId; // uint16 + /** + * This field shall be set to the InvokeID which was provided to the most recent command referencing this + * ActionID. + */ + public Integer invokeId; // uint32 + /** + * This field shall be set to state that the action is in at the time of generating the event. + */ + public ActionStateEnum newState; // ActionStateEnum + /** + * This field shall be set to indicate the reason for non-successful progress of the action. + */ + public ActionErrorEnum error; // ActionErrorEnum + + public ActionFailed(Integer actionId, Integer invokeId, ActionStateEnum newState, ActionErrorEnum error) { + this.actionId = actionId; + this.invokeId = invokeId; + this.newState = newState; + this.error = error; + } + } + + /** + * This data type holds the details of a single action, and contains the data fields below. + */ + public class ActionStruct { + /** + * This field shall provide an unique identifier used to identify an action. + */ + public Integer actionId; // uint16 + /** + * This field shall indicate the name (as assigned by the user or automatically by the server) associated with + * this action. This can be used for identifying the action to the user by the client. Example: "my + * colorful scene". + */ + public String name; // string + /** + * This field shall indicate the type of action. The value of Type of an action, along with its + * SupportedCommands can be used by the client in its UX or logic to determine how to present or use such + * action. See ActionTypeEnum for details and examples. + */ + public ActionTypeEnum type; // ActionTypeEnum + /** + * This field shall provide a reference to the associated endpoint list, which specifies the endpoints on this + * Node which will be impacted by this ActionID. + */ + public Integer endpointListId; // uint16 + /** + * This field is a bitmap which shall be used to indicate which of the cluster’s commands are supported for this + * particular action, with a bit set to 1 for each supported command according to the table below. Other bits + * shall be set to 0. + */ + public CommandBits supportedCommands; // CommandBits + /** + * This field shall indicate the current state of this action. + */ + public ActionStateEnum state; // ActionStateEnum + + public ActionStruct(Integer actionId, String name, ActionTypeEnum type, Integer endpointListId, + CommandBits supportedCommands, ActionStateEnum state) { + this.actionId = actionId; + this.name = name; + this.type = type; + this.endpointListId = endpointListId; + this.supportedCommands = supportedCommands; + this.state = state; + } + } + + /** + * This data type holds the details of a single endpoint list, which relates to a set of endpoints that have some + * logical relation, and contains the data fields below. + */ + public class EndpointListStruct { + /** + * This field shall provide an unique identifier used to identify the endpoint list. + */ + public Integer endpointListId; // uint16 + /** + * This field shall indicate the name (as assigned by the user or automatically by the server) associated with + * the set of endpoints in this list. This can be used for identifying the action to the user by the client. + * Example: "living room". + */ + public String name; // string + /** + * This field shall indicate the type of endpoint list, see EndpointListTypeEnum. + */ + public EndpointListTypeEnum type; // EndpointListTypeEnum + /** + * This field shall provide a list of endpoint numbers. + */ + public List endpoints; // list + + public EndpointListStruct(Integer endpointListId, String name, EndpointListTypeEnum type, + List endpoints) { + this.endpointListId = endpointListId; + this.name = name; + this.type = type; + this.endpoints = endpoints; + } + } + + // Enums + public enum ActionTypeEnum implements MatterEnum { + OTHER(0, "Other"), + SCENE(1, "Scene"), + SEQUENCE(2, "Sequence"), + AUTOMATION(3, "Automation"), + EXCEPTION(4, "Exception"), + NOTIFICATION(5, "Notification"), + ALARM(6, "Alarm"); + + public final Integer value; + public final String label; + + private ActionTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * Note that some of these states are applicable only for certain actions, as determined by their SupportedCommands. + */ + public enum ActionStateEnum implements MatterEnum { + INACTIVE(0, "Inactive"), + ACTIVE(1, "Active"), + PAUSED(2, "Paused"), + DISABLED(3, "Disabled"); + + public final Integer value; + public final String label; + + private ActionStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ActionErrorEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + INTERRUPTED(1, "Interrupted"); + + public final Integer value; + public final String label; + + private ActionErrorEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * The Room and Zone values are provided for the cases where a user (or the system on behalf of the user) has + * created logical grouping of the endpoints (e.g. bridged devices) based on location. + */ + public enum EndpointListTypeEnum implements MatterEnum { + OTHER(0, "Other"), + ROOM(1, "Room"), + ZONE(2, "Zone"); + + public final Integer value; + public final String label; + + private EndpointListTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + /** + * Note - The bit allocation of this bitmap shall follow the ID’s of the Commands of this cluster. + */ + public static class CommandBits { + public boolean instantAction; + public boolean instantActionWithTransition; + public boolean startAction; + public boolean startActionWithDuration; + public boolean stopAction; + public boolean pauseAction; + public boolean pauseActionWithDuration; + public boolean resumeAction; + public boolean enableAction; + public boolean enableActionWithDuration; + public boolean disableAction; + public boolean disableActionWithDuration; + + public CommandBits(boolean instantAction, boolean instantActionWithTransition, boolean startAction, + boolean startActionWithDuration, boolean stopAction, boolean pauseAction, + boolean pauseActionWithDuration, boolean resumeAction, boolean enableAction, + boolean enableActionWithDuration, boolean disableAction, boolean disableActionWithDuration) { + this.instantAction = instantAction; + this.instantActionWithTransition = instantActionWithTransition; + this.startAction = startAction; + this.startActionWithDuration = startActionWithDuration; + this.stopAction = stopAction; + this.pauseAction = pauseAction; + this.pauseActionWithDuration = pauseActionWithDuration; + this.resumeAction = resumeAction; + this.enableAction = enableAction; + this.enableActionWithDuration = enableActionWithDuration; + this.disableAction = disableAction; + this.disableActionWithDuration = disableActionWithDuration; + } + } + + public ActionsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 37, "Actions"); + } + + protected ActionsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command triggers an action (state change) on the involved endpoints, in a "fire and forget" + * manner. Afterwards, the action’s state shall be Inactive. + * Example: recall a scene on a number of lights. + */ + public static ClusterCommand instantAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("instantAction", map); + } + + /** + * It is recommended that, where possible (e.g., it is not possible for attributes with Boolean data type), a + * gradual transition SHOULD take place from the old to the new state over this time period. However, the exact + * transition is manufacturer dependent. + * This command triggers an action (state change) on the involved endpoints, with a specified time to transition + * from the current state to the new state. During the transition, the action’s state shall be Active. Afterwards, + * the action’s state shall be Inactive. + * Example: recall a scene on a number of lights, with a specified transition time. + */ + public static ClusterCommand instantActionWithTransition(Integer actionId, Integer invokeId, + Integer transitionTime) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + return new ClusterCommand("instantActionWithTransition", map); + } + + /** + * This command triggers the commencement of an action on the involved endpoints. Afterwards, the action’s state + * shall be Active. + * Example: start a dynamic lighting pattern (such as gradually rotating the colors around the setpoints of the + * scene) on a set of lights. + * Example: start a sequence of events such as a wake-up experience involving lights moving through several + * brightness/color combinations and the window covering gradually opening. + */ + public static ClusterCommand startAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("startAction", map); + } + + /** + * This command triggers the commencement of an action on the involved endpoints, and shall change the action’s + * state to Active. After the specified Duration, the action will stop, and the action’s state shall change to + * Inactive. + * Example: start a dynamic lighting pattern (such as gradually rotating the colors around the setpoints of the + * scene) on a set of lights for 1 hour (Duration=3600). + */ + public static ClusterCommand startActionWithDuration(Integer actionId, Integer invokeId, Integer duration) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + if (duration != null) { + map.put("duration", duration); + } + return new ClusterCommand("startActionWithDuration", map); + } + + /** + * This command stops the ongoing action on the involved endpoints. Afterwards, the action’s state shall be + * Inactive. + * Example: stop a dynamic lighting pattern which was previously started with StartAction. + */ + public static ClusterCommand stopAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("stopAction", map); + } + + /** + * This command pauses an ongoing action, and shall change the action’s state to Paused. + * Example: pause a dynamic lighting effect (the lights stay at their current color) which was previously started + * with StartAction. + */ + public static ClusterCommand pauseAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("pauseAction", map); + } + + /** + * This command pauses an ongoing action, and shall change the action’s state to Paused. After the specified + * Duration, the ongoing action will be automatically resumed. which shall change the action’s state to Active. + * Example: pause a dynamic lighting effect (the lights stay at their current color) for 10 minutes + * (Duration=600). + * The difference between Pause/Resume and Disable/Enable is on the one hand semantic (the former is more of a + * transitionary nature while the latter is more permanent) and on the other hand these can be implemented slightly + * differently in the implementation of the action (e.g. a Pause would be automatically resumed after some hours or + * during a nightly reset, while an Disable would remain in effect until explicitly enabled again). + */ + public static ClusterCommand pauseActionWithDuration(Integer actionId, Integer invokeId, Integer duration) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + if (duration != null) { + map.put("duration", duration); + } + return new ClusterCommand("pauseActionWithDuration", map); + } + + /** + * This command resumes a previously paused action, and shall change the action’s state to Active. + * The difference between ResumeAction and StartAction is that ResumeAction will continue the action from the state + * where it was paused, while StartAction will start the action from the beginning. + * Example: resume a dynamic lighting effect (the lights' colors will change gradually, continuing from the + * point they were paused). + */ + public static ClusterCommand resumeAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("resumeAction", map); + } + + /** + * This command enables a certain action or automation. Afterwards, the action’s state shall be Active. + * Example: enable a motion sensor to control the lights in an area. + */ + public static ClusterCommand enableAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("enableAction", map); + } + + /** + * This command enables a certain action or automation, and shall change the action’s state to be Active. After the + * specified Duration, the action or automation will stop, and the action’s state shall change to Disabled. + * Example: enable a "presence mimicking" behavior for the lights in your home during a vacation; the + * Duration field is used to indicated the length of your absence from home. After that period, the presence + * mimicking behavior will no longer control these lights. + */ + public static ClusterCommand enableActionWithDuration(Integer actionId, Integer invokeId, Integer duration) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + if (duration != null) { + map.put("duration", duration); + } + return new ClusterCommand("enableActionWithDuration", map); + } + + /** + * This command disables a certain action or automation, and shall change the action’s state to Inactive. + * Example: disable a motion sensor to no longer control the lights in an area. + */ + public static ClusterCommand disableAction(Integer actionId, Integer invokeId) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + return new ClusterCommand("disableAction", map); + } + + /** + * This command disables a certain action or automation, and shall change the action’s state to Disabled. After the + * specified Duration, the action or automation will re-start, and the action’s state shall change to either + * Inactive or Active, depending on the actions (see examples 4 and 6). + * Example: disable a "wakeup" experience for a period of 1 week when going on holiday (to prevent them + * from turning on in the morning while you’re not at home). After this period, the wakeup experience will control + * the lights as before. + */ + public static ClusterCommand disableActionWithDuration(Integer actionId, Integer invokeId, Integer duration) { + Map map = new LinkedHashMap<>(); + if (actionId != null) { + map.put("actionId", actionId); + } + if (invokeId != null) { + map.put("invokeId", invokeId); + } + if (duration != null) { + map.put("duration", duration); + } + return new ClusterCommand("disableActionWithDuration", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "actionList : " + actionList + "\n"; + str += "endpointLists : " + endpointLists + "\n"; + str += "setupUrl : " + setupUrl + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ActivatedCarbonFilterMonitoringCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ActivatedCarbonFilterMonitoringCluster.java new file mode 100644 index 00000000000..d46d6c190fb --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ActivatedCarbonFilterMonitoringCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ActivatedCarbonFilterMonitoring + * + * @author Dan Cunningham - Initial contribution + */ +public class ActivatedCarbonFilterMonitoringCluster extends ResourceMonitoringCluster { + + public static final int CLUSTER_ID = 0x0072; + public static final String CLUSTER_NAME = "ActivatedCarbonFilterMonitoring"; + public static final String CLUSTER_PREFIX = "activatedCarbonFilterMonitoring"; + + public ActivatedCarbonFilterMonitoringCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 114, "ActivatedCarbonFilterMonitoring"); + } + + protected ActivatedCarbonFilterMonitoringCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AdministratorCommissioningCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AdministratorCommissioningCluster.java new file mode 100644 index 00000000000..46d2ddce894 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AdministratorCommissioningCluster.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * AdministratorCommissioning + * + * @author Dan Cunningham - Initial contribution + */ +public class AdministratorCommissioningCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x003C; + public static final String CLUSTER_NAME = "AdministratorCommissioning"; + public static final String CLUSTER_PREFIX = "administratorCommissioning"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_WINDOW_STATUS = "windowStatus"; + public static final String ATTRIBUTE_ADMIN_FABRIC_INDEX = "adminFabricIndex"; + public static final String ATTRIBUTE_ADMIN_VENDOR_ID = "adminVendorId"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates whether a new Commissioning window has been opened by an Administrator, using either the + * OpenCommissioningWindow command or the OpenBasicCommissioningWindow command. + * This attribute shall revert to WindowNotOpen upon expiry of a commissioning window. + * > [!NOTE] + * > An initial commissioning window is not opened using either the OpenCommissioningWindow command or the + * OpenBasicCommissioningWindow command, and therefore this attribute shall be set to WindowNotOpen on initial + * commissioning. + */ + public CommissioningWindowStatusEnum windowStatus; // 0 CommissioningWindowStatusEnum R V + /** + * When the WindowStatus attribute is not set to WindowNotOpen, this attribute shall indicate the FabricIndex + * associated with the Fabric scoping of the Administrator that opened the window. This may be used to + * cross-reference in the Fabrics attribute of the Node Operational Credentials cluster. + * If, during an open commissioning window, the fabric for the Administrator that opened the window is removed, then + * this attribute shall be set to null. + * When the WindowStatus attribute is set to WindowNotOpen, this attribute shall be set to null. + */ + public Integer adminFabricIndex; // 1 fabric-idx R V + /** + * When the WindowStatus attribute is not set to WindowNotOpen, this attribute shall indicate the Vendor ID + * associated with the Fabric scoping of the Administrator that opened the window. This field shall match the + * VendorID field of the Fabrics attribute list entry associated with the Administrator having opened the window, at + * the time of window opening. If the fabric for the Administrator that opened the window is removed from the node + * while the commissioning window is still open, this attribute shall NOT be updated. + * When the WindowStatus attribute is set to WindowNotOpen, this attribute shall be set to null. + */ + public Integer adminVendorId; // 2 vendor-id R V + + // Enums + public enum CommissioningWindowStatusEnum implements MatterEnum { + WINDOW_NOT_OPEN(0, "Window Not Open"), + ENHANCED_WINDOW_OPEN(1, "Enhanced Window Open"), + BASIC_WINDOW_OPEN(2, "Basic Window Open"); + + public final Integer value; + public final String label; + + private CommissioningWindowStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusCodeEnum implements MatterEnum { + BUSY(2, "Busy"), + PAKE_PARAMETER_ERROR(3, "Pake Parameter Error"), + WINDOW_NOT_OPEN(4, "Window Not Open"); + + public final Integer value; + public final String label; + + private StatusCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Node supports Basic Commissioning Method. + */ + public boolean basic; + + public FeatureMap(boolean basic) { + this.basic = basic; + } + } + + public AdministratorCommissioningCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 60, "AdministratorCommissioning"); + } + + protected AdministratorCommissioningCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used by a current Administrator to instruct a Node to go into commissioning mode. The Enhanced + * Commissioning Method specifies a window of time during which an already commissioned Node accepts PASE sessions. + * The current Administrator MUST specify a timeout value for the duration of the OpenCommissioningWindow command. + * When the OpenCommissioningWindow command expires or commissioning completes, the Node shall remove the Passcode + * by deleting the PAKE passcode verifier as well as stop publishing the DNS-SD record corresponding to this command + * as described in Section 4.3.1, “Commissionable Node Discovery”. The commissioning into a new Fabric completes + * when the Node successfully receives a CommissioningComplete command, see Section 5.5, “Commissioning Flows”. + * The parameters for OpenCommissioningWindow command are as follows: + * A current Administrator may invoke this command to put a node in commissioning mode for the next Administrator. + * On completion, the command shall return a cluster specific status code from the Section 11.19.6, “Status Codes” + * below reflecting success or reasons for failure of the operation. The new Administrator shall discover the Node + * on the IP network using DNS-based Service Discovery (DNS-SD) for commissioning. + * If any format or validity errors related to the PAKEPasscodeVerifier, Iterations or Salt arguments arise, this + * command shall fail with a cluster specific status code of PAKEParameterError. + * If a commissioning window is already currently open, this command shall fail with a cluster specific status code + * of Busy. + * If the fail-safe timer is currently armed, this command shall fail with a cluster specific status code of Busy, + * since it is likely that concurrent commissioning operations from multiple separate Commissioners are about to + * take place. + * In case of any other parameter error, this command shall fail with a status code of COMMAND_INVALID. + */ + public static ClusterCommand openCommissioningWindow(Integer commissioningTimeout, OctetString pakePasscodeVerifier, + Integer discriminator, Integer iterations, OctetString salt) { + Map map = new LinkedHashMap<>(); + if (commissioningTimeout != null) { + map.put("commissioningTimeout", commissioningTimeout); + } + if (pakePasscodeVerifier != null) { + map.put("pakePasscodeVerifier", pakePasscodeVerifier); + } + if (discriminator != null) { + map.put("discriminator", discriminator); + } + if (iterations != null) { + map.put("iterations", iterations); + } + if (salt != null) { + map.put("salt", salt); + } + return new ClusterCommand("openCommissioningWindow", map); + } + + /** + * This command may be used by a current Administrator to instruct a Node to go into commissioning mode, if the node + * supports the Basic Commissioning Method. The Basic Commissioning Method specifies a window of time during which + * an already commissioned Node accepts PASE sessions. The current Administrator shall specify a timeout value for + * the duration of the OpenBasicCommissioningWindow command. + * If a commissioning window is already currently open, this command shall fail with a cluster specific status code + * of Busy. + * If the fail-safe timer is currently armed, this command shall fail with a cluster specific status code of Busy, + * since it is likely that concurrent commissioning operations from multiple separate Commissioners are about to + * take place. + * In case of any other parameter error, this command shall fail with a status code of COMMAND_INVALID. + * The commissioning into a new Fabric completes when the Node successfully receives a CommissioningComplete + * command, see Section 5.5, “Commissioning Flows”. The new Administrator shall discover the Node on the IP network + * using DNS-based Service Discovery (DNS-SD) for commissioning. + */ + public static ClusterCommand openBasicCommissioningWindow(Integer commissioningTimeout) { + Map map = new LinkedHashMap<>(); + if (commissioningTimeout != null) { + map.put("commissioningTimeout", commissioningTimeout); + } + return new ClusterCommand("openBasicCommissioningWindow", map); + } + + /** + * This command is used by a current Administrator to instruct a Node to revoke any active OpenCommissioningWindow + * or OpenBasicCommissioningWindow command. This is an idempotent command and the Node shall (for ECM) delete the + * temporary PAKEPasscodeVerifier and associated data, and stop publishing the DNS-SD record associated with the + * OpenCommissioningWindow or OpenBasicCommissioningWindow command, see Section 4.3.1, “Commissionable Node + * Discovery”. + * If no commissioning window was open at time of receipt, this command shall fail with a cluster specific status + * code of WindowNotOpen. + * If the commissioning window was open and the fail-safe was armed when this command is received, the device shall + * immediately expire the fail-safe and perform the cleanup steps outlined in Section 11.10.7.2.2, “Behavior on + * expiry of Fail-Safe timer”. + */ + public static ClusterCommand revokeCommissioning() { + return new ClusterCommand("revokeCommissioning"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "windowStatus : " + windowStatus + "\n"; + str += "adminFabricIndex : " + adminFabricIndex + "\n"; + str += "adminVendorId : " + adminVendorId + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AirQualityCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AirQualityCluster.java new file mode 100644 index 00000000000..b839903ee26 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AirQualityCluster.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * AirQuality + * + * @author Dan Cunningham - Initial contribution + */ +public class AirQualityCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x005B; + public static final String CLUSTER_NAME = "AirQuality"; + public static final String CLUSTER_PREFIX = "airQuality"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_AIR_QUALITY = "airQuality"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates a value from AirQualityEnum that is indicative of the currently measured air quality. + */ + public AirQualityEnum airQuality; // 0 AirQualityEnum R V + + // Enums + /** + * The AirQualityEnum provides a representation of the quality of the analyzed air. It is up to the device + * manufacturer to determine the mapping between the measured values and their corresponding enumeration values. + */ + public enum AirQualityEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + GOOD(1, "Good"), + FAIR(2, "Fair"), + MODERATE(3, "Moderate"), + POOR(4, "Poor"), + VERY_POOR(5, "Very Poor"), + EXTREMELY_POOR(6, "Extremely Poor"); + + public final Integer value; + public final String label; + + private AirQualityEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Cluster supports the Fair air quality level + */ + public boolean fair; + /** + * + * Cluster supports the Moderate air quality level + */ + public boolean moderate; + /** + * + * Cluster supports the Very poor air quality level + */ + public boolean veryPoor; + /** + * + * Cluster supports the Extremely poor air quality level + */ + public boolean extremelyPoor; + + public FeatureMap(boolean fair, boolean moderate, boolean veryPoor, boolean extremelyPoor) { + this.fair = fair; + this.moderate = moderate; + this.veryPoor = veryPoor; + this.extremelyPoor = extremelyPoor; + } + } + + public AirQualityCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 91, "AirQuality"); + } + + protected AirQualityCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "airQuality : " + airQuality + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AlarmBaseCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AlarmBaseCluster.java new file mode 100644 index 00000000000..e16e8b05a55 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AlarmBaseCluster.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * AlarmBase + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class AlarmBaseCluster extends BaseCluster { + + public static final String CLUSTER_NAME = "AlarmBase"; + public static final String CLUSTER_PREFIX = "alarmBase"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_MASK = "mask"; + public static final String ATTRIBUTE_LATCH = "latch"; + public static final String ATTRIBUTE_STATE = "state"; + public static final String ATTRIBUTE_SUPPORTED = "supported"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates a bitmap where each bit set in the Mask attribute corresponds to an alarm that shall be enabled. + */ + public AlarmBitmap mask; // 0 AlarmBitmap R V + /** + * Indicates a bitmap where each bit set in the Latch attribute shall indicate that the corresponding alarm will be + * latched when set, and will not reset to inactive when the underlying condition which caused the alarm is no + * longer present, and so requires an explicit reset using the Reset command. + */ + public AlarmBitmap latch; // 1 AlarmBitmap R V + /** + * Indicates a bitmap where each bit shall represent the state of an alarm. The value of true means the alarm is + * active, otherwise the alarm is inactive. + */ + public AlarmBitmap state; // 2 AlarmBitmap R V + /** + * Indicates a bitmap where each bit shall represent whether or not an alarm is supported. The value of true means + * the alarm is supported, otherwise the alarm is not supported. + * If an alarm is not supported, the corresponding bit in Mask, Latch, and State shall be false. + */ + public AlarmBitmap supported; // 3 AlarmBitmap R V + // Structs + + /** + * This event shall be generated when one or more alarms change state, and shall have these fields: + */ + public class Notify { + /** + * This field shall indicate those alarms that have become active. + */ + public AlarmBitmap active; // AlarmBitmap + /** + * This field shall indicate those alarms that have become inactive. + */ + public AlarmBitmap inactive; // AlarmBitmap + /** + * This field shall be a copy of the new State attribute value that resulted in the event being generated. That + * is, this field shall have all the bits in Active set and shall NOT have any of the bits in Inactive set. + */ + public AlarmBitmap state; // AlarmBitmap + /** + * This field shall be a copy of the Mask attribute when this event was generated. + */ + public AlarmBitmap mask; // AlarmBitmap + + public Notify(AlarmBitmap active, AlarmBitmap inactive, AlarmBitmap state, AlarmBitmap mask) { + this.active = active; + this.inactive = inactive; + this.state = state; + this.mask = mask; + } + } + + // Bitmaps + /** + * This data type shall be a map32 with values defined by the derived cluster. The meaning of each bit position + * shall be consistent for all attributes in a derived cluster. That is, if bit 0 is defined for an alarm, the + * Latch, State, and Supported information for that alarm are also bit 0. + */ + public static class AlarmBitmap { + public AlarmBitmap() { + } + } + + public static class FeatureMap { + /** + * + * This feature indicates that alarms can be reset via the Reset command. + */ + public boolean reset; + + public FeatureMap(boolean reset) { + this.reset = reset; + } + } + + protected AlarmBaseCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command resets active and latched alarms (if possible). Any generated Notify event shall contain fields that + * represent the state of the server after the command has been processed. + */ + public static ClusterCommand reset(AlarmBitmap alarms) { + Map map = new LinkedHashMap<>(); + if (alarms != null) { + map.put("alarms", alarms); + } + return new ClusterCommand("reset", map); + } + + /** + * This command allows a client to request that an alarm be enabled or suppressed at the server. + */ + public static ClusterCommand modifyEnabledAlarms(AlarmBitmap mask) { + Map map = new LinkedHashMap<>(); + if (mask != null) { + map.put("mask", mask); + } + return new ClusterCommand("modifyEnabledAlarms", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "mask : " + mask + "\n"; + str += "latch : " + latch + "\n"; + str += "state : " + state + "\n"; + str += "supported : " + supported + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ApplicationBasicCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ApplicationBasicCluster.java new file mode 100644 index 00000000000..f90f2f2bda2 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ApplicationBasicCluster.java @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ApplicationBasic + * + * @author Dan Cunningham - Initial contribution + */ +public class ApplicationBasicCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x050D; + public static final String CLUSTER_NAME = "ApplicationBasic"; + public static final String CLUSTER_PREFIX = "applicationBasic"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_VENDOR_NAME = "vendorName"; + public static final String ATTRIBUTE_VENDOR_ID = "vendorId"; + public static final String ATTRIBUTE_APPLICATION_NAME = "applicationName"; + public static final String ATTRIBUTE_PRODUCT_ID = "productId"; + public static final String ATTRIBUTE_APPLICATION = "application"; + public static final String ATTRIBUTE_STATUS = "status"; + public static final String ATTRIBUTE_APPLICATION_VERSION = "applicationVersion"; + public static final String ATTRIBUTE_ALLOWED_VENDOR_LIST = "allowedVendorList"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This attribute shall specify a human readable (displayable) name of the vendor for the Content App. + */ + public String vendorName; // 0 string R V + /** + * This attribute, if present, shall specify the Connectivity Standards Alliance assigned Vendor ID for the Content + * App. + */ + public Integer vendorId; // 1 vendor-id R V + /** + * This attribute shall specify a human readable (displayable) name of the Content App assigned by the vendor. For + * example, "NPR On Demand". The maximum length of the ApplicationName attribute is 256 bytes of UTF-8 + * characters. + */ + public String applicationName; // 2 string R V + /** + * This attribute, if present, shall specify a numeric ID assigned by the vendor to identify a specific Content App + * made by them. If the Content App is certified by the Connectivity Standards Alliance, then this would be the + * Product ID as specified by the vendor for the certification. + */ + public Integer productId; // 3 uint16 R V + /** + * This attribute shall specify a Content App which consists of an Application ID using a specified catalog. + */ + public ApplicationStruct application; // 4 ApplicationStruct R V + /** + * This attribute shall specify the current running status of the application. + */ + public ApplicationStatusEnum status; // 5 ApplicationStatusEnum R V + /** + * This attribute shall specify a human readable (displayable) version of the Content App assigned by the vendor. + * The maximum length of the ApplicationVersion attribute is 32 bytes of UTF-8 characters. + */ + public String applicationVersion; // 6 string R V + /** + * This attribute is a list of vendor IDs. Each entry is a vendor-id. + */ + public List allowedVendorList; // 7 list R A + // Structs + + /** + * This indicates a global identifier for an Application given a catalog. + */ + public class ApplicationStruct { + /** + * This field shall indicate the Connectivity Standards Alliance issued vendor ID for the catalog. The DIAL + * registry shall use value 0x0000. + * It is assumed that Content App Platform providers (see Video Player Architecture section in [MatterDevLib]) + * will have their own catalog vendor ID (set to their own Vendor ID) and will assign an ApplicationID to each + * Content App. + */ + public Integer catalogVendorId; // uint16 + /** + * This field shall indicate the application identifier, expressed as a string, such as "123456-5433", + * "PruneVideo" or "Company X". This field shall be unique within a catalog. + * For the DIAL registry catalog, this value shall be the DIAL prefix. + */ + public String applicationId; // string + + public ApplicationStruct(Integer catalogVendorId, String applicationId) { + this.catalogVendorId = catalogVendorId; + this.applicationId = applicationId; + } + } + + // Enums + public enum ApplicationStatusEnum implements MatterEnum { + STOPPED(0, "Stopped"), + ACTIVE_VISIBLE_FOCUS(1, "Active Visible Focus"), + ACTIVE_HIDDEN(2, "Active Hidden"), + ACTIVE_VISIBLE_NOT_FOCUS(3, "Active Visible Not Focus"); + + public final Integer value; + public final String label; + + private ApplicationStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public ApplicationBasicCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1293, "ApplicationBasic"); + } + + protected ApplicationBasicCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "vendorName : " + vendorName + "\n"; + str += "vendorId : " + vendorId + "\n"; + str += "applicationName : " + applicationName + "\n"; + str += "productId : " + productId + "\n"; + str += "application : " + application + "\n"; + str += "status : " + status + "\n"; + str += "applicationVersion : " + applicationVersion + "\n"; + str += "allowedVendorList : " + allowedVendorList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ApplicationLauncherCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ApplicationLauncherCluster.java new file mode 100644 index 00000000000..f3ec47b621b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ApplicationLauncherCluster.java @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ApplicationLauncher + * + * @author Dan Cunningham - Initial contribution + */ +public class ApplicationLauncherCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x050C; + public static final String CLUSTER_NAME = "ApplicationLauncher"; + public static final String CLUSTER_PREFIX = "applicationLauncher"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CATALOG_LIST = "catalogList"; + public static final String ATTRIBUTE_CURRENT_APP = "currentApp"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall specify the list of supported application catalogs, where each entry in the list is the + * CSA-issued vendor ID for the catalog. The DIAL registry (see [DIAL Registry]) shall use value 0x0000. + * It is expected that Content App Platform providers will have their own catalog vendor ID (set to their own Vendor + * ID) and will assign an ApplicationID to each Content App. + */ + public List catalogList; // 0 list R V + /** + * This attribute shall specify the current in-focus application, identified using an Application ID, catalog vendor + * ID and the corresponding endpoint number when the application is represented by a Content App endpoint. A null + * shall be used to indicate there is no current in-focus application. + */ + public ApplicationEPStruct currentApp; // 1 ApplicationEPStruct R V + // Structs + + /** + * This indicates a global identifier for an Application given a catalog. + */ + public class ApplicationStruct { + /** + * This field shall indicate the CSA-issued vendor ID for the catalog. The DIAL registry shall use value 0x0000. + * Content App Platform providers will have their own catalog vendor ID (set to their own Vendor ID) and will + * assign an ApplicationID to each Content App. + */ + public Integer catalogVendorId; // uint16 + /** + * This field shall indicate the application identifier, expressed as a string, such as "PruneVideo" + * or "Company X". This field shall be unique within a catalog. + * For the DIAL registry catalog, this value shall be the DIAL prefix (see [DIAL Registry]). + */ + public String applicationId; // string + + public ApplicationStruct(Integer catalogVendorId, String applicationId) { + this.catalogVendorId = catalogVendorId; + this.applicationId = applicationId; + } + } + + /** + * This specifies an app along with its corresponding endpoint. + */ + public class ApplicationEPStruct { + public ApplicationStruct application; // ApplicationStruct + public Integer endpoint; // endpoint-no + + public ApplicationEPStruct(ApplicationStruct application, Integer endpoint) { + this.application = application; + this.endpoint = endpoint; + } + } + + // Enums + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + APP_NOT_AVAILABLE(1, "App Not Available"), + SYSTEM_BUSY(2, "System Busy"), + PENDING_USER_APPROVAL(3, "Pending User Approval"), + DOWNLOADING(4, "Downloading"), + INSTALLING(5, "Installing"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Support for attributes and commands required for endpoint to support launching any application within the + * supported application catalogs + */ + public boolean applicationPlatform; + + public FeatureMap(boolean applicationPlatform) { + this.applicationPlatform = applicationPlatform; + } + } + + public ApplicationLauncherCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1292, "ApplicationLauncher"); + } + + protected ApplicationLauncherCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt of this command, the server shall launch the application with optional data. The application shall + * be either + * • the specified application, if the Application Platform feature is supported; + * • otherwise the application corresponding to the endpoint. + * The endpoint shall launch and bring to foreground the requisite application if the application is not already + * launched and in foreground. The Status attribute shall be updated to ActiveVisibleFocus on the Application Basic + * cluster of the Endpoint corresponding to the launched application. The Status attribute shall be updated on any + * other application whose Status may have changed as a result of this command. The CurrentApp attribute, if + * supported, shall be updated to reflect the new application in the foreground. + * This command returns a Launcher Response. + */ + public static ClusterCommand launchApp(ApplicationStruct application, OctetString data) { + Map map = new LinkedHashMap<>(); + if (application != null) { + map.put("application", application); + } + if (data != null) { + map.put("data", data); + } + return new ClusterCommand("launchApp", map); + } + + /** + * Upon receipt of this command, the server shall stop the application if it is running. The application shall be + * either + * • the specified application, if the Application Platform feature is supported; + * • otherwise the application corresponding to the endpoint. + * The Status attribute shall be updated to Stopped on the Application Basic cluster of the Endpoint corresponding + * to the stopped application. The Status attribute shall be updated on any other application whose Status may have + * changed as a result of this command. + * This command returns a Launcher Response. + */ + public static ClusterCommand stopApp(ApplicationStruct application) { + Map map = new LinkedHashMap<>(); + if (application != null) { + map.put("application", application); + } + return new ClusterCommand("stopApp", map); + } + + /** + * Upon receipt of this command, the server shall hide the application. The application shall be either + * • the specified application, if the Application Platform feature is supported; + * • otherwise the application corresponding to the endpoint. + * The endpoint may decide to stop the application based on manufacturer specific behavior or resource constraints + * if any. The Status attribute shall be updated to ActiveHidden or Stopped, depending on the action taken, on the + * Application Basic cluster of the Endpoint corresponding to the application on which the action was taken. The + * Status attribute shall be updated on any other application whose Status may have changed as a result of this + * command. This command returns a Launcher Response. + */ + public static ClusterCommand hideApp(ApplicationStruct application) { + Map map = new LinkedHashMap<>(); + if (application != null) { + map.put("application", application); + } + return new ClusterCommand("hideApp", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "catalogList : " + catalogList + "\n"; + str += "currentApp : " + currentApp + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AudioOutputCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AudioOutputCluster.java new file mode 100644 index 00000000000..769c32785ab --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/AudioOutputCluster.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * AudioOutput + * + * @author Dan Cunningham - Initial contribution + */ +public class AudioOutputCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x050B; + public static final String CLUSTER_NAME = "AudioOutput"; + public static final String CLUSTER_PREFIX = "audioOutput"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_OUTPUT_LIST = "outputList"; + public static final String ATTRIBUTE_CURRENT_OUTPUT = "currentOutput"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute provides the list of outputs supported by the device. + */ + public List outputList; // 0 list R V + /** + * This attribute contains the value of the index field of the currently selected OutputInfoStruct. + */ + public Integer currentOutput; // 1 uint8 R V + // Structs + + /** + * This contains information about an output. + */ + public class OutputInfoStruct { + /** + * This field shall indicate the unique index into the list of outputs. + */ + public Integer index; // uint8 + /** + * This field shall indicate the type of output. + */ + public OutputTypeEnum outputType; // OutputTypeEnum + /** + * The device defined and user editable output name, such as “Soundbar”, “Speakers”. This field may be blank, + * but SHOULD be provided when known. + */ + public String name; // string + + public OutputInfoStruct(Integer index, OutputTypeEnum outputType, String name) { + this.index = index; + this.outputType = outputType; + this.name = name; + } + } + + // Enums + /** + * The type of output, expressed as an enum, with the following values: + */ + public enum OutputTypeEnum implements MatterEnum { + HDMI(0, "Hdmi"), + BT(1, "Bt"), + OPTICAL(2, "Optical"), + HEADPHONE(3, "Headphone"), + INTERNAL(4, "Internal"), + OTHER(5, "Other"); + + public final Integer value; + public final String label; + + private OutputTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Supports updates to output names + */ + public boolean nameUpdates; + + public FeatureMap(boolean nameUpdates) { + this.nameUpdates = nameUpdates; + } + } + + public AudioOutputCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1291, "AudioOutput"); + } + + protected AudioOutputCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this shall change the output on the device to the output at a specific index in the Output List. + * Note that when the current output is set to an output of type HDMI, adjustments to volume via a Speaker endpoint + * on the same node may cause HDMI volume up/down commands to be sent to the given HDMI output. + */ + public static ClusterCommand selectOutput(Integer index) { + Map map = new LinkedHashMap<>(); + if (index != null) { + map.put("index", index); + } + return new ClusterCommand("selectOutput", map); + } + + /** + * Upon receipt, this shall rename the output at a specific index in the Output List. + * Updates to the output name shall appear in the device’s settings menus. Name updates may automatically be sent to + * the actual device to which the output connects. + */ + public static ClusterCommand renameOutput(Integer index, String name) { + Map map = new LinkedHashMap<>(); + if (index != null) { + map.put("index", index); + } + if (name != null) { + map.put("name", name); + } + return new ClusterCommand("renameOutput", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "outputList : " + outputList + "\n"; + str += "currentOutput : " + currentOutput + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BallastConfigurationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BallastConfigurationCluster.java new file mode 100644 index 00000000000..e5ec10582d5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BallastConfigurationCluster.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * BallastConfiguration + * + * @author Dan Cunningham - Initial contribution + */ +public class BallastConfigurationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0301; + public static final String CLUSTER_NAME = "BallastConfiguration"; + public static final String CLUSTER_PREFIX = "ballastConfiguration"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_PHYSICAL_MIN_LEVEL = "physicalMinLevel"; + public static final String ATTRIBUTE_PHYSICAL_MAX_LEVEL = "physicalMaxLevel"; + public static final String ATTRIBUTE_BALLAST_STATUS = "ballastStatus"; + public static final String ATTRIBUTE_MIN_LEVEL = "minLevel"; + public static final String ATTRIBUTE_MAX_LEVEL = "maxLevel"; + public static final String ATTRIBUTE_INTRINSIC_BALLAST_FACTOR = "intrinsicBallastFactor"; + public static final String ATTRIBUTE_BALLAST_FACTOR_ADJUSTMENT = "ballastFactorAdjustment"; + public static final String ATTRIBUTE_LAMP_QUANTITY = "lampQuantity"; + public static final String ATTRIBUTE_LAMP_TYPE = "lampType"; + public static final String ATTRIBUTE_LAMP_MANUFACTURER = "lampManufacturer"; + public static final String ATTRIBUTE_LAMP_RATED_HOURS = "lampRatedHours"; + public static final String ATTRIBUTE_LAMP_BURN_HOURS = "lampBurnHours"; + public static final String ATTRIBUTE_LAMP_ALARM_MODE = "lampAlarmMode"; + public static final String ATTRIBUTE_LAMP_BURN_HOURS_TRIP_POINT = "lampBurnHoursTripPoint"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This attribute shall specify the minimum light output the ballast can achieve according to the dimming light + * curve (see Dimming Curve). + */ + public Integer physicalMinLevel; // 0 uint8 R V + /** + * This attribute shall specify the maximum light output the ballast can achieve according to the dimming light + * curve (see Dimming Curve). + */ + public Integer physicalMaxLevel; // 1 uint8 R V + /** + * This attribute shall specify the status of various aspects of the ballast or the connected lights, see + * BallastStatusBitmap. + */ + public BallastStatusBitmap ballastStatus; // 2 BallastStatusBitmap R V + /** + * This attribute shall specify the light output of the ballast according to the dimming light curve (see Dimming + * Curve) when the Level Control Cluster’s CurrentLevel attribute equals to 1 (and the On/Off Cluster’s OnOff + * attribute equals to TRUE). + * The value of this attribute shall be both greater than or equal to PhysicalMinLevel and less than or equal to + * MaxLevel. If an attempt is made to set this attribute to a level where these conditions are not met, a response + * shall be returned with status code set to CONSTRAINT_ERROR, and the level shall NOT be set. + */ + public Integer minLevel; // 16 uint8 RW VM + /** + * This attribute shall specify the light output of the ballast according to the dimming light curve (see Dimming + * Curve) when the Level Control Cluster’s CurrentLevel attribute equals to 254 (and the On/Off Cluster’s OnOff + * attribute equals to TRUE). + * The value of this attribute shall be both less than or equal to PhysicalMaxLevel and greater than or equal to + * MinLevel. If an attempt is made to set this attribute to a level where these conditions are not met, a response + * shall be returned with status code set to CONSTRAINT_ERROR, and the level shall NOT be set. + */ + public Integer maxLevel; // 17 uint8 RW VM + /** + * This attribute shall specify the ballast factor, as a percentage, of the ballast/lamp combination, prior to any + * adjustment. + * A value of null indicates in invalid value. + */ + public Integer intrinsicBallastFactor; // 20 uint8 RW VM + /** + * This attribute shall specify the multiplication factor, as a percentage, to be applied to the configured light + * output of the lamps. A typical use for this attribute is to compensate for reduction in efficiency over the + * lifetime of a lamp. + * ### The light output is given by + * actual light output = configured light output x BallastFactorAdjustment / 100% + * The range for this attribute is manufacturer dependent. If an attempt is made to set this attribute to a level + * that cannot be supported, a response shall be returned with status code set to CONSTRAINT_ERROR, and the level + * shall NOT be changed. The value of null indicates that ballast factor scaling is not in use. + */ + public Integer ballastFactorAdjustment; // 21 uint8 RW VM + /** + * This attribute shall specify the number of lamps connected to this ballast. (Note 1: this number does not take + * into account whether lamps are actually in their sockets or not). + */ + public Integer lampQuantity; // 32 uint8 R V + /** + * This attribute shall specify the type of lamps (including their wattage) connected to the ballast. + */ + public String lampType; // 48 string RW VM + /** + * This attribute shall specify the name of the manufacturer of the currently connected lamps. + */ + public String lampManufacturer; // 49 string RW VM + /** + * This attribute shall specify the number of hours of use the lamps are rated for by the manufacturer. + * A value of null indicates an invalid or unknown time. + */ + public Integer lampRatedHours; // 50 uint24 RW VM + /** + * This attribute shall specify the length of time, in hours, the currently connected lamps have been operated, + * cumulative since the last re-lamping. Burn hours shall NOT be accumulated if the lamps are off. + * This attribute SHOULD be reset to zero (e.g., remotely) when the lamps are changed. If partially used lamps are + * connected, LampBurnHours SHOULD be updated to reflect the burn hours of the lamps. + * A value of null indicates an invalid or unknown time. + */ + public Integer lampBurnHours; // 51 uint24 RW VM + /** + * This attribute shall specify which attributes may cause an alarm notification to be generated. Ain each bit + * position means that its associated attribute is able to generate an alarm. + */ + public LampAlarmModeBitmap lampAlarmMode; // 52 LampAlarmModeBitmap RW VM + /** + * This attribute shall specify the number of hours the LampBurnHours attribute may reach before an alarm is + * generated. + * If the Alarms cluster is not present on the same device this attribute is not used and thus may be omitted (see + * Dependencies). + * The Alarm Code field included in the generated alarm shall be 0x01. + * If this attribute has the value of null, then this alarm shall NOT be generated. + */ + public Integer lampBurnHoursTripPoint; // 53 uint24 RW VM + + // Bitmaps + public static class BallastStatusBitmap { + /** + * Operational state of the ballast. + * This bit shall indicate whether the ballast is operational. + * • 0 = The ballast is fully operational + * • 1 = The ballast is not fully operational + */ + public boolean ballastNonOperational; + /** + * Operational state of the lamps. + * This bit shall indicate whether all lamps is operational. + * • 0 = All lamps are operational + * • 1 = One or more lamp is not in its socket or is faulty + */ + public boolean lampFailure; + + public BallastStatusBitmap(boolean ballastNonOperational, boolean lampFailure) { + this.ballastNonOperational = ballastNonOperational; + this.lampFailure = lampFailure; + } + } + + public static class LampAlarmModeBitmap { + /** + * State of LampBurnHours alarm generation + * This bit shall indicate that the LampBurnHours attribute may generate an alarm. + */ + public boolean lampBurnHours; + + public LampAlarmModeBitmap(boolean lampBurnHours) { + this.lampBurnHours = lampBurnHours; + } + } + + public BallastConfigurationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 769, "BallastConfiguration"); + } + + protected BallastConfigurationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "physicalMinLevel : " + physicalMinLevel + "\n"; + str += "physicalMaxLevel : " + physicalMaxLevel + "\n"; + str += "ballastStatus : " + ballastStatus + "\n"; + str += "minLevel : " + minLevel + "\n"; + str += "maxLevel : " + maxLevel + "\n"; + str += "intrinsicBallastFactor : " + intrinsicBallastFactor + "\n"; + str += "ballastFactorAdjustment : " + ballastFactorAdjustment + "\n"; + str += "lampQuantity : " + lampQuantity + "\n"; + str += "lampType : " + lampType + "\n"; + str += "lampManufacturer : " + lampManufacturer + "\n"; + str += "lampRatedHours : " + lampRatedHours + "\n"; + str += "lampBurnHours : " + lampBurnHours + "\n"; + str += "lampAlarmMode : " + lampAlarmMode + "\n"; + str += "lampBurnHoursTripPoint : " + lampBurnHoursTripPoint + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BaseCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BaseCluster.java new file mode 100644 index 00000000000..d958f847d32 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BaseCluster.java @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +import com.google.gson.Gson; + +/** + * undefined + * + * @author Dan Cunningham - Initial contribution + */ + +public class BaseCluster { + + protected static final Gson GSON = new Gson(); + public BigInteger nodeId; + public int endpointId; + public int id; + public String name; + + public interface MatterEnum { + Integer getValue(); + + String getLabel(); + + public static E fromValue(Class enumClass, int value) { + E[] constants = enumClass.getEnumConstants(); + if (constants != null) { + for (E enumConstant : constants) { + if (enumConstant != null) { + if (enumConstant.getValue().equals(value)) { + return enumConstant; + } + } + } + } + throw new IllegalArgumentException("Unknown value: " + value); + } + } + + public BaseCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + this.nodeId = nodeId; + this.endpointId = endpointId; + this.id = clusterId; + this.name = clusterName; + } + + public static class OctetString { + public byte[] value; + + public OctetString(byte[] value) { + this.value = value; + } + + public OctetString(String hexString) { + int length = hexString.length(); + value = new byte[length / 2]; + for (int i = 0; i < length; i += 2) { + value[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + + Character.digit(hexString.charAt(i + 1), 16)); + } + } + + public @NonNull String toHexString() { + StringBuilder hexString = new StringBuilder(); + for (byte b : value) { + String hex = Integer.toHexString(0xFF & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + public @NonNull String toString() { + return toHexString(); + } + } + + // Structs + public class AtomicAttributeStatusStruct { + public Integer attributeId; // attrib-id + public Integer statusCode; // status + + public AtomicAttributeStatusStruct(Integer attributeId, Integer statusCode) { + this.attributeId = attributeId; + this.statusCode = statusCode; + } + } + + public class MeasurementAccuracyRangeStruct { + public BigInteger rangeMin; // int64 + public BigInteger rangeMax; // int64 + public Integer percentMax; // percent100ths + public Integer percentMin; // percent100ths + public Integer percentTypical; // percent100ths + public BigInteger fixedMax; // uint64 + public BigInteger fixedMin; // uint64 + public BigInteger fixedTypical; // uint64 + + public MeasurementAccuracyRangeStruct(BigInteger rangeMin, BigInteger rangeMax, Integer percentMax, + Integer percentMin, Integer percentTypical, BigInteger fixedMax, BigInteger fixedMin, + BigInteger fixedTypical) { + this.rangeMin = rangeMin; + this.rangeMax = rangeMax; + this.percentMax = percentMax; + this.percentMin = percentMin; + this.percentTypical = percentTypical; + this.fixedMax = fixedMax; + this.fixedMin = fixedMin; + this.fixedTypical = fixedTypical; + } + } + + public class MeasurementAccuracyStruct { + public MeasurementTypeEnum measurementType; // MeasurementTypeEnum + public Boolean measured; // bool + public BigInteger minMeasuredValue; // int64 + public BigInteger maxMeasuredValue; // int64 + public List accuracyRanges; // list + + public MeasurementAccuracyStruct(MeasurementTypeEnum measurementType, Boolean measured, + BigInteger minMeasuredValue, BigInteger maxMeasuredValue, + List accuracyRanges) { + this.measurementType = measurementType; + this.measured = measured; + this.minMeasuredValue = minMeasuredValue; + this.maxMeasuredValue = maxMeasuredValue; + this.accuracyRanges = accuracyRanges; + } + } + + public class Date { + public Integer year; // uint8 + public Integer month; // uint8 + public Integer day; // uint8 + public Integer dayOfWeek; // uint8 + + public Date(Integer year, Integer month, Integer day, Integer dayOfWeek) { + this.year = year; + this.month = month; + this.day = day; + this.dayOfWeek = dayOfWeek; + } + } + + public class Locationdesc { + public String locationName; // string + public Integer floorNumber; // int16 + public Integer areaType; // tag + + public Locationdesc(String locationName, Integer floorNumber, Integer areaType) { + this.locationName = locationName; + this.floorNumber = floorNumber; + this.areaType = areaType; + } + } + + public class Semtag { + public Integer mfgCode; // vendor-id + public Integer namespaceId; // namespace + public Integer tag; // tag + public String label; // string + + public Semtag(Integer mfgCode, Integer namespaceId, Integer tag, String label) { + this.mfgCode = mfgCode; + this.namespaceId = namespaceId; + this.tag = tag; + this.label = label; + } + } + + public class Tod { + public Integer hours; // uint8 + public Integer minutes; // uint8 + public Integer seconds; // uint8 + public Integer hundredths; // uint8 + + public Tod(Integer hours, Integer minutes, Integer seconds, Integer hundredths) { + this.hours = hours; + this.minutes = minutes; + this.seconds = seconds; + this.hundredths = hundredths; + } + } + + // Enums + public enum AtomicRequestTypeEnum implements MatterEnum { + BEGIN_WRITE(0, "BeginWrite"), + COMMIT_WRITE(1, "CommitWrite"), + ROLLBACK_WRITE(2, "RollbackWrite"); + + public final Integer value; + public final String label; + + private AtomicRequestTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum MeasurementTypeEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + VOLTAGE(1, "Voltage"), + ACTIVE_CURRENT(2, "ActiveCurrent"), + REACTIVE_CURRENT(3, "ReactiveCurrent"), + APPARENT_CURRENT(4, "ApparentCurrent"), + ACTIVE_POWER(5, "ActivePower"), + REACTIVE_POWER(6, "ReactivePower"), + APPARENT_POWER(7, "ApparentPower"), + RMS_VOLTAGE(8, "RmsVoltage"), + RMS_CURRENT(9, "RmsCurrent"), + RMS_POWER(10, "RmsPower"), + FREQUENCY(11, "Frequency"), + POWER_FACTOR(12, "PowerFactor"), + NEUTRAL_CURRENT(13, "NeutralCurrent"), + ELECTRICAL_ENERGY(14, "ElectricalEnergy"); + + public final Integer value; + public final String label; + + private MeasurementTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SoftwareVersionCertificationStatusEnum implements MatterEnum { + DEV_TEST(0, "DevTest"), + PROVISIONAL(1, "Provisional"), + CERTIFIED(2, "Certified"), + REVOKED(3, "Revoked"); + + public final Integer value; + public final String label; + + private SoftwareVersionCertificationStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum Priority implements MatterEnum { + DEBUG(0, "Debug"), + INFO(1, "Info"), + CRITICAL(2, "Critical"); + + public final Integer value; + public final String label; + + private Priority(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum Status implements MatterEnum { + SUCCESS(0, "Success"), + FAILURE(1, "Failure"), + INVALID_SUBSCRIPTION(125, "InvalidSubscription"), + UNSUPPORTED_ACCESS(126, "UnsupportedAccess"), + UNSUPPORTED_ENDPOINT(127, "UnsupportedEndpoint"), + INVALID_ACTION(128, "InvalidAction"), + UNSUPPORTED_COMMAND(129, "UnsupportedCommand"), + INVALID_COMMAND(133, "InvalidCommand"), + UNSUPPORTED_ATTRIBUTE(134, "UnsupportedAttribute"), + CONSTRAINT_ERROR(135, "ConstraintError"), + UNSUPPORTED_WRITE(136, "UnsupportedWrite"), + RESOURCE_EXHAUSTED(137, "ResourceExhausted"), + NOT_FOUND(139, "NotFound"), + UNREPORTABLE_ATTRIBUTE(140, "UnreportableAttribute"), + INVALID_DATA_TYPE(141, "InvalidDataType"), + UNSUPPORTED_READ(143, "UnsupportedRead"), + DATA_VERSION_MISMATCH(146, "DataVersionMismatch"), + TIMEOUT(148, "Timeout"), + UNSUPPORTED_NODE(155, "UnsupportedNode"), + BUSY(156, "Busy"), + ACCESS_RESTRICTED(157, "AccessRestricted"), + UNSUPPORTED_CLUSTER(195, "UnsupportedCluster"), + NO_UPSTREAM_SUBSCRIPTION(197, "NoUpstreamSubscription"), + NEEDS_TIMED_INTERACTION(198, "NeedsTimedInteraction"), + UNSUPPORTED_EVENT(199, "UnsupportedEvent"), + PATHS_EXHAUSTED(200, "PathsExhausted"), + TIMED_REQUEST_MISMATCH(201, "TimedRequestMismatch"), + FAILSAFE_REQUIRED(202, "FailsafeRequired"), + INVALID_IN_STATE(203, "InvalidInState"), + NO_COMMAND_RESPONSE(204, "NoCommandResponse"), + TERMS_AND_CONDITIONS_CHANGED(205, "TermsAndConditionsChanged"), + MAINTENANCE_REQUIRED(206, "MaintenanceRequired"); + + public final Integer value; + public final String label; + + private Status(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class WildcardPathFlagsBitmap { + public boolean wildcardSkipRootNode; + public boolean wildcardSkipGlobalAttributes; + public boolean wildcardSkipAttributeList; + public boolean reserved; + public boolean wildcardSkipCommandLists; + public boolean wildcardSkipCustomElements; + public boolean wildcardSkipFixedAttributes; + public boolean wildcardSkipChangesOmittedAttributes; + public boolean wildcardSkipDiagnosticsClusters; + + public WildcardPathFlagsBitmap(boolean wildcardSkipRootNode, boolean wildcardSkipGlobalAttributes, + boolean wildcardSkipAttributeList, boolean reserved, boolean wildcardSkipCommandLists, + boolean wildcardSkipCustomElements, boolean wildcardSkipFixedAttributes, + boolean wildcardSkipChangesOmittedAttributes, boolean wildcardSkipDiagnosticsClusters) { + this.wildcardSkipRootNode = wildcardSkipRootNode; + this.wildcardSkipGlobalAttributes = wildcardSkipGlobalAttributes; + this.wildcardSkipAttributeList = wildcardSkipAttributeList; + this.reserved = reserved; + this.wildcardSkipCommandLists = wildcardSkipCommandLists; + this.wildcardSkipCustomElements = wildcardSkipCustomElements; + this.wildcardSkipFixedAttributes = wildcardSkipFixedAttributes; + this.wildcardSkipChangesOmittedAttributes = wildcardSkipChangesOmittedAttributes; + this.wildcardSkipDiagnosticsClusters = wildcardSkipDiagnosticsClusters; + } + } + + public static class FeatureMap { + public List map; + + public FeatureMap(List map) { + this.map = map; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BasicInformationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BasicInformationCluster.java new file mode 100644 index 00000000000..2d0a054234c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BasicInformationCluster.java @@ -0,0 +1,459 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * BasicInformation + * + * @author Dan Cunningham - Initial contribution + */ +public class BasicInformationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0028; + public static final String CLUSTER_NAME = "BasicInformation"; + public static final String CLUSTER_PREFIX = "basicInformation"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_DATA_MODEL_REVISION = "dataModelRevision"; + public static final String ATTRIBUTE_VENDOR_NAME = "vendorName"; + public static final String ATTRIBUTE_VENDOR_ID = "vendorId"; + public static final String ATTRIBUTE_PRODUCT_NAME = "productName"; + public static final String ATTRIBUTE_PRODUCT_ID = "productId"; + public static final String ATTRIBUTE_NODE_LABEL = "nodeLabel"; + public static final String ATTRIBUTE_LOCATION = "location"; + public static final String ATTRIBUTE_HARDWARE_VERSION = "hardwareVersion"; + public static final String ATTRIBUTE_HARDWARE_VERSION_STRING = "hardwareVersionString"; + public static final String ATTRIBUTE_SOFTWARE_VERSION = "softwareVersion"; + public static final String ATTRIBUTE_SOFTWARE_VERSION_STRING = "softwareVersionString"; + public static final String ATTRIBUTE_MANUFACTURING_DATE = "manufacturingDate"; + public static final String ATTRIBUTE_PART_NUMBER = "partNumber"; + public static final String ATTRIBUTE_PRODUCT_URL = "productUrl"; + public static final String ATTRIBUTE_PRODUCT_LABEL = "productLabel"; + public static final String ATTRIBUTE_SERIAL_NUMBER = "serialNumber"; + public static final String ATTRIBUTE_LOCAL_CONFIG_DISABLED = "localConfigDisabled"; + public static final String ATTRIBUTE_REACHABLE = "reachable"; + public static final String ATTRIBUTE_UNIQUE_ID = "uniqueId"; + public static final String ATTRIBUTE_CAPABILITY_MINIMA = "capabilityMinima"; + public static final String ATTRIBUTE_PRODUCT_APPEARANCE = "productAppearance"; + public static final String ATTRIBUTE_SPECIFICATION_VERSION = "specificationVersion"; + public static final String ATTRIBUTE_MAX_PATHS_PER_INVOKE = "maxPathsPerInvoke"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This attribute shall be set to the revision number of the Data Model against which the Node is certified. The + * value of this attribute shall be one of the valid values listed in Section 7.1.1, “Revision History”. + */ + public Integer dataModelRevision; // 0 uint16 R V + /** + * This attribute shall specify a human readable (displayable) name of the vendor for the Node. + */ + public String vendorName; // 1 string R V + /** + * This attribute shall specify the Vendor ID. + */ + public Integer vendorId; // 2 vendor-id R V + /** + * This attribute shall specify a human readable (displayable) name of the model for the Node such as the model + * number (or other identifier) assigned by the vendor. + */ + public String productName; // 3 string R V + /** + * This attribute shall specify the Product ID assigned by the vendor that is unique to the specific product of the + * Node. + */ + public Integer productId; // 4 uint16 R V + /** + * Indicates a user defined name for the Node. This attribute SHOULD be set during initial commissioning and may be + * updated by further reconfigurations. + */ + public String nodeLabel; // 5 string RW VM + /** + * This attribute shall be an ISO 3166-1 alpha-2 code to represent the country, dependent territory, or special area + * of geographic interest in which the Node is located at the time of the attribute being set. This attribute shall + * be set during initial commissioning (unless already set) and may be updated by further reconfigurations. This + * attribute may affect some regulatory aspects of the Node’s operation, such as radio transmission power levels in + * given spectrum allocation bands if technologies where this is applicable are used. The Location’s region code + * shall be interpreted in a case-insensitive manner. If the Node cannot understand the location code with which it + * was configured, or the location code has not yet been configured, it shall configure itself in a region-agnostic + * manner as determined by the vendor, avoiding region-specific assumptions as much as is practical. The special + * value XX shall indicate that region-agnostic mode is used. + */ + public String location; // 6 string RW VA + /** + * This attribute shall specify the version number of the hardware of the Node. The meaning of its value, and the + * versioning scheme, are vendor defined. + */ + public Integer hardwareVersion; // 7 uint16 R V + /** + * This attribute shall specify the version number of the hardware of the Node. The meaning of its value, and the + * versioning scheme, are vendor defined. The HardwareVersionString attribute shall be used to provide a more + * user-friendly value than that represented by the HardwareVersion attribute. + */ + public String hardwareVersionString; // 8 string R V + /** + * This attribute shall contain the current version number for the software running on this Node. The version number + * can be compared using a total ordering to determine if a version is logically newer than another one. A larger + * value of SoftwareVersion is newer than a lower value, from the perspective of software updates (see Section + * 11.20.3.3, “Availability of Software Images”). Nodes may query this field to determine the currently running + * version of software on another given Node. + */ + public Integer softwareVersion; // 9 uint32 R V + /** + * This attribute shall contain a current human-readable representation for the software running on the Node. This + * version information may be conveyed to users. The maximum length of the SoftwareVersionString attribute is 64 + * bytes of UTF-8 characters. The contents SHOULD only use simple 7-bit ASCII alphanumeric and punctuation + * characters, so as to simplify the conveyance of the value to a variety of cultures. + * Examples of version strings include "1.0", "1.2.3456", "1.2-2", + * "1.0b123", "1.2_3". + */ + public String softwareVersionString; // 10 string R V + /** + * This attribute shall specify the date that the Node was manufactured. The first 8 characters shall specify the + * date of manufacture of the Node in international date notation according to ISO 8601, i.e., YYYYMMDD, e.g., + * 20060814. The final 8 characters may include country, factory, line, shift or other related information at the + * option of the vendor. The format of this information is vendor defined. + */ + public String manufacturingDate; // 11 string R V + /** + * This attribute shall specify a human-readable (displayable) vendor assigned part number for the Node whose + * meaning and numbering scheme is vendor defined. + * Multiple products (and hence PartNumbers) can share a ProductID. For instance, there may be different packaging + * (with different PartNumbers) for different regions; also different colors of a product might share the ProductID + * but may have a different PartNumber. + */ + public String partNumber; // 12 string R V + /** + * This attribute shall specify a link to a product specific web page. The specified URL SHOULD resolve to a + * maintained web page available for the lifetime of the product. The syntax of this attribute shall follow the + * syntax as specified in RFC 1738 and shall use the https scheme. The maximum length of this attribute is 256 ASCII + * characters. + */ + public String productUrl; // 13 string R V + /** + * This attribute shall specify a vendor specific human readable (displayable) product label. The ProductLabel + * attribute may be used to provide a more user-friendly value than that represented by the ProductName attribute. + * The ProductLabel attribute SHOULD NOT include the name of the vendor as defined within the VendorName attribute. + */ + public String productLabel; // 14 string R V + /** + * This attribute shall specify a human readable (displayable) serial number. + */ + public String serialNumber; // 15 string R V + /** + * This attribute shall allow a local Node configuration to be disabled. When this attribute is set to True the Node + * shall disable the ability to configure the Node through an on-Node user interface. The value of the + * LocalConfigDisabled attribute shall NOT in any way modify, disable, or otherwise affect the user’s ability to + * trigger a factory reset on the Node. + */ + public Boolean localConfigDisabled; // 16 bool RW VM + /** + * This attribute (when used) shall indicate whether the Node can be reached. For a native Node this is implicitly + * True (and its use is optional). + * Its main use case is in the derived Bridged Device Basic Information cluster where it is used to indicate whether + * the bridged device is reachable by the bridge over the non-native network. + */ + public Boolean reachable; // 17 bool R V + /** + * Indicates a unique identifier for the device, which is constructed in a manufacturer specific manner. + * It may be constructed using a permanent device identifier (such as device MAC address) as basis. In order to + * prevent tracking, + * • it SHOULD NOT be identical to (or easily derived from) such permanent device identifier + * • it shall be updated when the device is factory reset + * • it shall NOT be identical to the SerialNumber attribute + * • it shall NOT be printed on the product or delivered with the product + * The value does not need to be human readable, since it is intended for machine to machine (M2M) communication. + * > [!NOTE] + * > The conformance of the UniqueID attribute was optional in cluster revisions prior to revision 4. + * This UniqueID attribute shall NOT be the same as the Persistent Unique ID which is used in the Rotating Device + * Identifier mechanism. + */ + public String uniqueId; // 18 string R V + /** + * This attribute shall provide the minimum guaranteed value for some system-wide resource capabilities that are not + * otherwise cluster-specific and do not appear elsewhere. This attribute may be used by clients to optimize + * communication with Nodes by allowing them to use more than the strict minimum values required by this + * specification, wherever available. + * The values supported by the server in reality may be larger than the values provided in this attribute, such as + * if a server is not resource-constrained at all. However, clients SHOULD only rely on the amounts provided in this + * attribute. + * Note that since the fixed values within this attribute may change over time, both increasing and decreasing, as + * software versions change for a given Node, clients SHOULD take care not to assume forever unchanging values and + * SHOULD NOT cache this value permanently at Commissioning time. + */ + public CapabilityMinimaStruct capabilityMinima; // 19 CapabilityMinimaStruct R V + /** + * This attribute shall provide information about the appearance of the product, which could be useful to a user + * trying to locate or identify the node. + */ + public ProductAppearanceStruct productAppearance; // 20 ProductAppearanceStruct R V + /** + * This attribute shall contain the current version number for the specification version this Node was certified + * against. The version number can be compared using a total ordering to determine if a version is logically newer + * than another one. A larger value of SpecificationVersion is newer than a lower value. + * Nodes may query this field to determine the currently supported version of the specification on another given + * Node. + * The format of this number is segmented as its four component bytes. Bit positions for the fields are as follows: + * For example, a SpecificationVersion value of 0x0102AA00 is composed of 4 version components, representing a + * version 1.2.170.0. + * In the example above: + * • Major version is the uppermost byte (0x01). + * • Minor version is the following byte (0x02). + * • Patch version is 170/0xAA. + * • Reserved1 value is 0. + * The initial revision (1.0) of this specification (1.0) was 0x01000000. Matter Spring 2024 release (1.3) was + * 0x01030000. + * If the SpecificationVersion is absent or zero, such as in Basic Information cluster revisions prior to Revision + * 3, the specification version cannot be properly inferred unless other heuristics are employed. + * Comparison of SpecificationVersion shall always include the total value over 32 bits, without masking reserved + * parts. + */ + public Integer specificationVersion; // 21 uint32 R V + /** + * Indicates the maximum number of elements in a single InvokeRequests list (see Section 8.8.2, “Invoke Request + * Action”) that the Node is able to process. Note that since this attribute may change over time, both increasing + * and decreasing, as software versions change for a given Node, clients SHOULD take care not to assume forever + * unchanging values and SHOULD NOT cache this value permanently at Commissioning time. + * If the MaxPathsPerInvoke attribute is absent or zero, such as in Basic Information cluster revisions prior to + * Revision 3, clients shall assume a value of 1. + */ + public Integer maxPathsPerInvoke; // 22 uint16 R V + // Structs + + /** + * The StartUp event shall be generated by a Node as soon as reasonable after completing a boot or reboot process. + * The StartUp event SHOULD be the first Data Model event recorded by the Node after it completes a boot or reboot + * process. + */ + public class StartUp { + /** + * This field shall be set to the same value as the one available in the SoftwareVersion attribute. + */ + public Integer softwareVersion; // uint32 + + public StartUp(Integer softwareVersion) { + this.softwareVersion = softwareVersion; + } + } + + /** + * The ShutDown event SHOULD be generated by a Node prior to any orderly shutdown sequence on a best-effort basis. + * When a ShutDown event is generated, it SHOULD be the last Data Model event recorded by the Node. This event + * SHOULD be delivered urgently to current subscribers on a best-effort basis. Any subsequent incoming interactions + * to the Node may be dropped until the completion of a future boot or reboot process. + */ + public class ShutDown { + public ShutDown() { + } + } + + /** + * The Leave event SHOULD be generated by a Node prior to permanently leaving a given Fabric, such as when the + * RemoveFabric command is invoked for a given fabric, or triggered by factory reset or some other manufacturer + * specific action to disable or reset the operational data in the Node. When a Leave event is generated, it SHOULD + * be assumed that the fabric recorded in the event is no longer usable, and subsequent interactions targeting that + * fabric will most likely fail. + * Upon receipt of Leave Event on a subscription, the receiving Node may update other nodes in the fabric by + * removing related bindings, access control list entries and other data referencing the leaving Node. + */ + public class Leave { + /** + * This field shall contain the local Fabric Index of the fabric which the node is about to leave. + */ + public Integer fabricIndex; // fabric-idx + + public Leave(Integer fabricIndex) { + this.fabricIndex = fabricIndex; + } + } + + /** + * This event shall be supported if and only if the Reachable attribute is supported. + * This event (when supported) shall be generated when there is a change in the Reachable attribute. + * Its main use case is in the derived Bridged Device Basic Information cluster. + */ + public class ReachableChanged { + /** + * This field shall indicate the value of the Reachable attribute after it was changed. + */ + public Boolean reachableNewValue; // bool + + public ReachableChanged(Boolean reachableNewValue) { + this.reachableNewValue = reachableNewValue; + } + } + + /** + * This structure provides a description of the product’s appearance. + */ + public class ProductAppearanceStruct { + /** + * This field shall indicate the visible finish of the product. + */ + public ProductFinishEnum finish; // ProductFinishEnum + /** + * This field indicates the representative color of the visible parts of the product. If the product has no + * representative color, the field shall be null. + */ + public ColorEnum primaryColor; // ColorEnum + + public ProductAppearanceStruct(ProductFinishEnum finish, ColorEnum primaryColor) { + this.finish = finish; + this.primaryColor = primaryColor; + } + } + + /** + * This structure provides constant values related to overall global capabilities of this Node, that are not + * cluster-specific. + */ + public class CapabilityMinimaStruct { + /** + * This field shall indicate the actual minimum number of concurrent CASE sessions that are supported per + * fabric. + * This value shall NOT be smaller than the required minimum indicated in Section 4.14.2.8, “Minimal Number of + * CASE Sessions”. + */ + public Integer caseSessionsPerFabric; // uint16 + /** + * This field shall indicate the actual minimum number of concurrent subscriptions supported per fabric. + * This value shall NOT be smaller than the required minimum indicated in Section 8.5.1, “Subscribe + * Transaction”. + */ + public Integer subscriptionsPerFabric; // uint16 + + public CapabilityMinimaStruct(Integer caseSessionsPerFabric, Integer subscriptionsPerFabric) { + this.caseSessionsPerFabric = caseSessionsPerFabric; + this.subscriptionsPerFabric = subscriptionsPerFabric; + } + } + + // Enums + /** + * The data type of ProductFinishEnum is derived from enum8. + */ + public enum ProductFinishEnum implements MatterEnum { + OTHER(0, "Other"), + MATTE(1, "Matte"), + SATIN(2, "Satin"), + POLISHED(3, "Polished"), + RUGGED(4, "Rugged"), + FABRIC(5, "Fabric"); + + public final Integer value; + public final String label; + + private ProductFinishEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * The data type of ColorEnum is derived from enum8. + */ + public enum ColorEnum implements MatterEnum { + BLACK(0, "Black"), + NAVY(1, "Navy"), + GREEN(2, "Green"), + TEAL(3, "Teal"), + MAROON(4, "Maroon"), + PURPLE(5, "Purple"), + OLIVE(6, "Olive"), + GRAY(7, "Gray"), + BLUE(8, "Blue"), + LIME(9, "Lime"), + AQUA(10, "Aqua"), + RED(11, "Red"), + FUCHSIA(12, "Fuchsia"), + YELLOW(13, "Yellow"), + WHITE(14, "White"), + NICKEL(15, "Nickel"), + CHROME(16, "Chrome"), + BRASS(17, "Brass"), + COPPER(18, "Copper"), + SILVER(19, "Silver"), + GOLD(20, "Gold"); + + public final Integer value; + public final String label; + + private ColorEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public BasicInformationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 40, "BasicInformation"); + } + + protected BasicInformationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "dataModelRevision : " + dataModelRevision + "\n"; + str += "vendorName : " + vendorName + "\n"; + str += "vendorId : " + vendorId + "\n"; + str += "productName : " + productName + "\n"; + str += "productId : " + productId + "\n"; + str += "nodeLabel : " + nodeLabel + "\n"; + str += "location : " + location + "\n"; + str += "hardwareVersion : " + hardwareVersion + "\n"; + str += "hardwareVersionString : " + hardwareVersionString + "\n"; + str += "softwareVersion : " + softwareVersion + "\n"; + str += "softwareVersionString : " + softwareVersionString + "\n"; + str += "manufacturingDate : " + manufacturingDate + "\n"; + str += "partNumber : " + partNumber + "\n"; + str += "productUrl : " + productUrl + "\n"; + str += "productLabel : " + productLabel + "\n"; + str += "serialNumber : " + serialNumber + "\n"; + str += "localConfigDisabled : " + localConfigDisabled + "\n"; + str += "reachable : " + reachable + "\n"; + str += "uniqueId : " + uniqueId + "\n"; + str += "capabilityMinima : " + capabilityMinima + "\n"; + str += "productAppearance : " + productAppearance + "\n"; + str += "specificationVersion : " + specificationVersion + "\n"; + str += "maxPathsPerInvoke : " + maxPathsPerInvoke + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BindingCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BindingCluster.java new file mode 100644 index 00000000000..6039fb9a8e0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BindingCluster.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Binding + * + * @author Dan Cunningham - Initial contribution + */ +public class BindingCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x001E; + public static final String CLUSTER_NAME = "Binding"; + public static final String CLUSTER_PREFIX = "binding"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_BINDING = "binding"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Each entry shall represent a binding. + */ + public List binding; // 0 list RW F VM + // Structs + + public class TargetStruct { + /** + * This field is the remote target node ID. If the Endpoint field is present, this field shall be present. + */ + public BigInteger node; // node-id + /** + * This field is the target group ID that represents remote endpoints. If the Endpoint field is present, this + * field shall NOT be present. + */ + public Integer group; // group-id + /** + * This field is the remote endpoint that the local endpoint is bound to. If the Group field is present, this + * field shall NOT be present. + */ + public Integer endpoint; // endpoint-no + /** + * This field is the cluster ID (client & server) on the local and target endpoint(s). If this field is + * present, the client cluster shall also exist on this endpoint (with this Binding cluster). If this field is + * present, the target shall be this cluster on the target endpoint(s). + */ + public Integer cluster; // cluster-id + public Integer fabricIndex; // FabricIndex + + public TargetStruct(BigInteger node, Integer group, Integer endpoint, Integer cluster, Integer fabricIndex) { + this.node = node; + this.group = group; + this.endpoint = endpoint; + this.cluster = cluster; + this.fabricIndex = fabricIndex; + } + } + + public BindingCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 30, "Binding"); + } + + protected BindingCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "binding : " + binding + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BooleanStateCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BooleanStateCluster.java new file mode 100644 index 00000000000..12e936ed517 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BooleanStateCluster.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * BooleanState + * + * @author Dan Cunningham - Initial contribution + */ +public class BooleanStateCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0045; + public static final String CLUSTER_NAME = "BooleanState"; + public static final String CLUSTER_PREFIX = "booleanState"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_STATE_VALUE = "stateValue"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This represents a boolean state. + * The semantics of this boolean state are defined by the device type using this cluster. + * For example, in a Contact Sensor device type, FALSE=open or no contact, TRUE=closed or contact. + */ + public Boolean stateValue; // 0 bool R V + // Structs + + /** + * If this event is supported, it shall be generated when the StateValue attribute changes. + */ + public class StateChange { + /** + * This field shall indicate the new value of the StateValue attribute. + */ + public Boolean stateValue; // bool + + public StateChange(Boolean stateValue) { + this.stateValue = stateValue; + } + } + + public BooleanStateCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 69, "BooleanState"); + } + + protected BooleanStateCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "stateValue : " + stateValue + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BooleanStateConfigurationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BooleanStateConfigurationCluster.java new file mode 100644 index 00000000000..3118bc64129 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BooleanStateConfigurationCluster.java @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * BooleanStateConfiguration + * + * @author Dan Cunningham - Initial contribution + */ +public class BooleanStateConfigurationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0080; + public static final String CLUSTER_NAME = "BooleanStateConfiguration"; + public static final String CLUSTER_PREFIX = "booleanStateConfiguration"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CURRENT_SENSITIVITY_LEVEL = "currentSensitivityLevel"; + public static final String ATTRIBUTE_SUPPORTED_SENSITIVITY_LEVELS = "supportedSensitivityLevels"; + public static final String ATTRIBUTE_DEFAULT_SENSITIVITY_LEVEL = "defaultSensitivityLevel"; + public static final String ATTRIBUTE_ALARMS_ACTIVE = "alarmsActive"; + public static final String ATTRIBUTE_ALARMS_SUPPRESSED = "alarmsSuppressed"; + public static final String ATTRIBUTE_ALARMS_ENABLED = "alarmsEnabled"; + public static final String ATTRIBUTE_ALARMS_SUPPORTED = "alarmsSupported"; + public static final String ATTRIBUTE_SENSOR_FAULT = "sensorFault"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the currently selected sensitivity level. + * If a write interaction to this attribute contains an unsupported sensitivity value, a CONSTRAINT_ERROR status + * shall be returned. + */ + public Integer currentSensitivityLevel; // 0 uint8 RW VO + /** + * Indicates the number of supported sensitivity levels by the device. + * These supported sensitivity levels shall be ordered by sensitivity, where a value of 0 shall be considered the + * lowest sensitivity level (least sensitive) and the highest supported value shall be considered the highest + * sensitivity level. + * The number of supported sensitivity levels SHOULD represent unique sensitivity levels supported by the device. + */ + public Integer supportedSensitivityLevels; // 1 uint8 R V + /** + * Indicates the default sensitivity level selected by the manufacturer. + */ + public Integer defaultSensitivityLevel; // 2 uint8 R V + /** + * Indicates which specific alarm modes on the server are currently active. When the sensor is no longer triggered, + * this attribute shall be set to the inactive state, by setting the bit to 0, for all supported alarm modes. + * If an alarm mode is not supported, the bit indicating this alarm mode shall always be 0. A bit shall indicate + * whether the alarm mode inactive or not: + * • 0 = Inactive + * • 1 = Active + */ + public AlarmModeBitmap alarmsActive; // 3 AlarmModeBitmap R V + /** + * Indicates which specific alarm modes on the server are currently suppressed. When the sensor is no longer + * triggered, this attribute shall be set to the unsuppressed state, by setting the bit to 0, for all supported + * alarm modes. + * If an alarm mode is not supported, the bit indicating this alarm mode shall always be 0. A bit shall indicate + * whether the alarm mode is suppressed or not: + * • 0 = Not suppressed + * • 1 = Suppressed + */ + public AlarmModeBitmap alarmsSuppressed; // 4 AlarmModeBitmap R V + /** + * Indicates the alarm modes that will be emitted if the sensor is triggered. + * If an alarm mode is not supported, the bit indicating this alarm mode shall always be 0. A bit shall indicate + * whether the alarm mode is enabled or disabled: + * • 0 = Disabled + * • 1 = Enabled + */ + public AlarmModeBitmap alarmsEnabled; // 5 AlarmModeBitmap R V + /** + * Indicates the alarms supported by the sensor. A bit shall indicate whether the alarm mode is supported: + * • 0 = Not supported + * • 1 = Supported + */ + public AlarmModeBitmap alarmsSupported; // 6 AlarmModeBitmap R V + /** + * Indicates any faults registered by the device. + */ + public SensorFaultBitmap sensorFault; // 7 SensorFaultBitmap R V + // Structs + + /** + * This event shall be generated after any bits in the AlarmsActive and/or AlarmsSuppressed attributes change. This + * may occur in situations such as when internal processing by the server determines that an alarm mode becomes + * active or inactive, or when the SuppressAlarm or EnableDisableAlarm commands are processed in a way that some + * alarm modes becomes suppressed, active or inactive. + * If several alarm modes change state at the same time, a single event combining multiple changes may be emitted + * instead of multiple events each representing a single change. + */ + public class AlarmsStateChanged { + /** + * This field shall indicate the state of active alarm modes, as indicated by the AlarmsActive attribute, at the + * time the event was generated. + */ + public AlarmModeBitmap alarmsActive; // AlarmModeBitmap + /** + * This field shall indicate the state of suppressed alarm modes, as indicated by the AlarmsSuppressed + * attribute, at the time the event was generated. + */ + public AlarmModeBitmap alarmsSuppressed; // AlarmModeBitmap + + public AlarmsStateChanged(AlarmModeBitmap alarmsActive, AlarmModeBitmap alarmsSuppressed) { + this.alarmsActive = alarmsActive; + this.alarmsSuppressed = alarmsSuppressed; + } + } + + /** + * This event shall be generated when the device registers or clears a fault. + */ + public class SensorFault { + /** + * This field shall indicate the value of the SensorFault attribute, at the time this event is generated. + */ + public SensorFaultBitmap sensorFault; // SensorFaultBitmap + + public SensorFault(SensorFaultBitmap sensorFault) { + this.sensorFault = sensorFault; + } + } + + // Bitmaps + public static class AlarmModeBitmap { + public boolean visual; + public boolean audible; + + public AlarmModeBitmap(boolean visual, boolean audible) { + this.visual = visual; + this.audible = audible; + } + } + + public static class SensorFaultBitmap { + public boolean generalFault; + + public SensorFaultBitmap(boolean generalFault) { + this.generalFault = generalFault; + } + } + + public static class FeatureMap { + /** + * + * Supports visual alarms + */ + public boolean visual; + /** + * + * Supports audible alarms + */ + public boolean audible; + /** + * + * This feature shall indicate that the device is able to suppress the supported alarm modes, when the user + * acknowledges the alarm. This is intended to stop visual and/or audible alarms, when the user has become aware + * that the sensor is triggered, but it is no longer desired to have the alarm modes active on the device, e.g.: + * • The triggering cause have been resolved by the user, but the sensor has not yet stopped detecting the + * triggering cause. + * • The user is not able to address the triggering cause, but is aware of the alarm and suppress/acknowledge it + * be addressed at a later point. + * Acknowledge of alarms will for the remainder of this cluster be referred to as suppress. + * A suppressed alarm is still considered active and will remain so unless it is actively disabled or the + * triggering condition is not longer present. The action of suppressing an alarm mode is only applicable to and + * is intended to stop the physical alarming, e.g. emitting a sound or blinking a light; it does not impact + * alarm reporting in AlarmsActive. + */ + public boolean alarmSuppress; + /** + * + * Supports ability to set sensor sensitivity + */ + public boolean sensitivityLevel; + + public FeatureMap(boolean visual, boolean audible, boolean alarmSuppress, boolean sensitivityLevel) { + this.visual = visual; + this.audible = audible; + this.alarmSuppress = alarmSuppress; + this.sensitivityLevel = sensitivityLevel; + } + } + + public BooleanStateConfigurationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 128, "BooleanStateConfiguration"); + } + + protected BooleanStateConfigurationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand suppressAlarm(AlarmModeBitmap alarmsToSuppress) { + Map map = new LinkedHashMap<>(); + if (alarmsToSuppress != null) { + map.put("alarmsToSuppress", alarmsToSuppress); + } + return new ClusterCommand("suppressAlarm", map); + } + + public static ClusterCommand enableDisableAlarm(AlarmModeBitmap alarmsToEnableDisable) { + Map map = new LinkedHashMap<>(); + if (alarmsToEnableDisable != null) { + map.put("alarmsToEnableDisable", alarmsToEnableDisable); + } + return new ClusterCommand("enableDisableAlarm", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "currentSensitivityLevel : " + currentSensitivityLevel + "\n"; + str += "supportedSensitivityLevels : " + supportedSensitivityLevels + "\n"; + str += "defaultSensitivityLevel : " + defaultSensitivityLevel + "\n"; + str += "alarmsActive : " + alarmsActive + "\n"; + str += "alarmsSuppressed : " + alarmsSuppressed + "\n"; + str += "alarmsEnabled : " + alarmsEnabled + "\n"; + str += "alarmsSupported : " + alarmsSupported + "\n"; + str += "sensorFault : " + sensorFault + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BridgedDeviceBasicInformationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BridgedDeviceBasicInformationCluster.java new file mode 100644 index 00000000000..7261fdff9c1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/BridgedDeviceBasicInformationCluster.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * BridgedDeviceBasicInformation + * + * @author Dan Cunningham - Initial contribution + */ +public class BridgedDeviceBasicInformationCluster extends BasicInformationCluster { + + public static final int CLUSTER_ID = 0x0039; + public static final String CLUSTER_NAME = "BridgedDeviceBasicInformation"; + public static final String CLUSTER_PREFIX = "bridgedDeviceBasicInformation"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + + public FeatureMap featureMap; // 65532 FeatureMap + // Structs + + public class StartUp { + public StartUp() { + } + } + + public class ShutDown { + public ShutDown() { + } + } + + /** + * The Leave event SHOULD be generated by the bridge when it detects that the associated device has left the + * non-Matter network. + * > [!NOTE] + * > The FabricIndex field has the X conformance, indicating it shall NOT be present. This event, in the context + * of Bridged Device Basic Information cluster, has no usable fields, but the original Basic Information cluster’s + * field definition is kept for completeness. + */ + public class Leave { + public String fabricIndex; // + + public Leave(String fabricIndex) { + this.fabricIndex = fabricIndex; + } + } + + /** + * This event shall be generated when there is a change in the Reachable attribute. Its purpose is to provide an + * indication towards interested parties that the reachability of a bridged device has changed over its native + * connectivity technology, so they may take appropriate action. + * After (re)start of a bridge this event may be generated. + */ + public class ReachableChanged { + public ReachableChanged() { + } + } + + /** + * This event (when supported) shall be generated the next time a bridged device becomes active after a KeepActive + * command is received. + * See KeepActive for more details. + */ + public class ActiveChanged { + /** + * This field shall indicate the minimum duration, in milliseconds, that the bridged device will remain active + * after receiving the initial request from the KeepActive processing steps. + * If the bridged device is a Matter Intermittently Connected Device, PromisedActiveDuration shall be set to the + * PromisedActiveDuration value returned in the StayActiveResponse command. + * If the bridged device is not a Matter Intermittently Connected Device, the implementation of this is + * best-effort since it may interact with non-native protocol. + */ + public Integer promisedActiveDuration; // uint32 + + public ActiveChanged(Integer promisedActiveDuration) { + this.promisedActiveDuration = promisedActiveDuration; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Support bridged ICDs. + */ + public boolean bridgedIcdSupport; + + public FeatureMap(boolean bridgedIcdSupport) { + this.bridgedIcdSupport = bridgedIcdSupport; + } + } + + public BridgedDeviceBasicInformationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 57, "BridgedDeviceBasicInformation"); + } + + protected BridgedDeviceBasicInformationCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, the server shall attempt to keep the bridged device active for the duration specified by the + * command, when the device is next active. + * The implementation of this is best-effort since it may interact with non-native protocols. However, several + * specific protocol requirements are: + * • If the bridged device is a Matter Intermittently Connected Device, then the server shall send a + * StayActiveRequest command with the StayActiveDuration field set to value of the StayActiveDuration field in the + * received command to the bridged device when the bridged device next sends a checks-in message or subscription + * report. See Intermittently Connected Devices Behavior for details on ICD state management. + * When the bridge detects that the bridged device goes into an active state, an ActiveChanged event shall be + * generated. + * In order to avoid unnecessary power consumption in the bridged device: + * • The server shall enter a "pending active" state for the associated device when the KeepActive command + * is received. The server "pending active" state shall expire after the amount of time defined by the + * TimeoutMs field, in milliseconds, if no subsequent KeepActive command is received. When a KeepActive command is + * received, the "pending active" state is set, the StayActiveDuration is updated to the greater of the + * new value and the previously stored value, and the TimeoutMs is updated to the greater of the new value and the + * remaining time until the prior "pending active" state expires. + * • The server shall only keep the bridged device active once for a request. (The server shall only consider the + * operation performed if an associated ActiveChanged event was generated.) + */ + public static ClusterCommand keepActive(Integer stayActiveDuration, Integer timeoutMs) { + Map map = new LinkedHashMap<>(); + if (stayActiveDuration != null) { + map.put("stayActiveDuration", stayActiveDuration); + } + if (timeoutMs != null) { + map.put("timeoutMs", timeoutMs); + } + return new ClusterCommand("keepActive", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "featureMap : " + featureMap + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CarbonDioxideConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CarbonDioxideConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..15d72b90a0f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CarbonDioxideConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * CarbonDioxideConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class CarbonDioxideConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x040D; + public static final String CLUSTER_NAME = "CarbonDioxideConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "carbonDioxideConcentrationMeasurement"; + + public CarbonDioxideConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1037, "CarbonDioxideConcentrationMeasurement"); + } + + protected CarbonDioxideConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CarbonMonoxideConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CarbonMonoxideConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..d82f304a1b0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CarbonMonoxideConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * CarbonMonoxideConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class CarbonMonoxideConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x040C; + public static final String CLUSTER_NAME = "CarbonMonoxideConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "carbonMonoxideConcentrationMeasurement"; + + public CarbonMonoxideConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1036, "CarbonMonoxideConcentrationMeasurement"); + } + + protected CarbonMonoxideConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ChannelCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ChannelCluster.java new file mode 100644 index 00000000000..22f980bd5de --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ChannelCluster.java @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * Channel + * + * @author Dan Cunningham - Initial contribution + */ +public class ChannelCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0504; + public static final String CLUSTER_NAME = "Channel"; + public static final String CLUSTER_PREFIX = "channel"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CHANNEL_LIST = "channelList"; + public static final String ATTRIBUTE_LINEUP = "lineup"; + public static final String ATTRIBUTE_CURRENT_CHANNEL = "currentChannel"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall provide the list of supported channels. + */ + public List channelList; // 0 list R V + /** + * This attribute shall identify the channel lineup using external data sources. + */ + public LineupInfoStruct lineup; // 1 LineupInfoStruct R V + /** + * This attribute shall contain the current channel. When supported but a channel is not currently tuned to (if a + * content application is in foreground), the value of the field shall be null. + */ + public ChannelInfoStruct currentChannel; // 2 ChannelInfoStruct R V + // Structs + + /** + * This indicates a channel in a channel lineup. + * While the major and minor numbers in the ChannelInfoStruct support use of ATSC channel format, a lineup may use + * other formats which can map into these numeric values. + */ + public class ChannelInfoStruct { + /** + * This field shall indicate the channel major number value (for example, using ATSC format). When the channel + * number is expressed as a string, such as "13.1" or "256", the major number would be 13 or + * 256, respectively. This field is required but shall be set to 0 for channels such as over-the-top channels + * that are not represented by a major or minor number. + */ + public Integer majorNumber; // uint16 + /** + * This field shall indicate the channel minor number value (for example, using ATSC format). When the channel + * number is expressed as a string, such as "13.1" or "256", the minor number would be 1 or + * 0, respectively. This field is required but shall be set to 0 for channels such as over-the-top channels that + * are not represented by a major or minor number. + */ + public Integer minorNumber; // uint16 + /** + * This field shall indicate the marketing name for the channel, such as “The CW" or "Comedy + * Central". This field is optional, but SHOULD be provided when known. + */ + public String name; // string + /** + * This field shall indicate the call sign of the channel, such as "PBS". This field is optional, but + * SHOULD be provided when known. + */ + public String callSign; // string + /** + * This field shall indicate the local affiliate call sign, such as "KCTS". This field is optional, + * but SHOULD be provided when known. + */ + public String affiliateCallSign; // string + /** + * This shall indicate the unique identifier for a specific channel. This field is optional, but SHOULD be + * provided when MajorNumber and MinorNumber are not available. + */ + public String identifier; // string + /** + * This shall indicate the type or grouping of a specific channel. This field is optional, but SHOULD be + * provided when known. + */ + public ChannelTypeEnum type; // ChannelTypeEnum + + public ChannelInfoStruct(Integer majorNumber, Integer minorNumber, String name, String callSign, + String affiliateCallSign, String identifier, ChannelTypeEnum type) { + this.majorNumber = majorNumber; + this.minorNumber = minorNumber; + this.name = name; + this.callSign = callSign; + this.affiliateCallSign = affiliateCallSign; + this.identifier = identifier; + this.type = type; + } + } + + /** + * The Lineup Info allows references to external lineup sources like Gracenote. The combination of OperatorName, + * LineupName, and PostalCode MUST uniquely identify a lineup. + */ + public class LineupInfoStruct { + /** + * This field shall indicate the name of the operator, for example “Comcast”. + */ + public String operatorName; // string + /** + * This field shall indicate the name of the provider lineup, for example "Comcast King County". This + * field is optional, but SHOULD be provided when known. + */ + public String lineupName; // string + /** + * This field shall indicate the postal code (zip code) for the location of the device, such as + * "98052". This field is optional, but SHOULD be provided when known. + */ + public String postalCode; // string + /** + * This field shall indicate the type of lineup. This field is optional, but SHOULD be provided when known. + */ + public LineupInfoTypeEnum lineupInfoType; // LineupInfoTypeEnum + + public LineupInfoStruct(String operatorName, String lineupName, String postalCode, + LineupInfoTypeEnum lineupInfoType) { + this.operatorName = operatorName; + this.lineupName = lineupName; + this.postalCode = postalCode; + this.lineupInfoType = lineupInfoType; + } + } + + /** + * This indicates a program within an electronic program guide (EPG). + */ + public class ProgramStruct { + /** + * This field shall indicate a unique identifier for a program within an electronic program guide list. The + * identifier shall be unique across multiple channels. + */ + public String identifier; // string + /** + * This field shall indicate the channel associated to the program. + */ + public ChannelInfoStruct channel; // ChannelInfoStruct + /** + * This field shall indicate an epoch time in seconds indicating the start time of a program, as a UTC time. + * This field can represent a past or future value. + */ + public Integer startTime; // epoch-s + /** + * This field shall indicate an epoch time in seconds indicating the end time of a program, as a UTC time. This + * field can represent a past or future value but shall be greater than the StartTime. + */ + public Integer endTime; // epoch-s + /** + * This field shall indicate the title or name for the specific program. For example, “MCIS: Los Angeles”. + */ + public String title; // string + /** + * This field shall indicate the subtitle for the specific program. For example, “Maybe Today" which is an + * episode name for “MCIS: Los Angeles”. This field is optional but shall be provided if applicable and known. + */ + public String subtitle; // string + /** + * This field shall indicate the brief description for the specific program. For example, a description of an + * episode. This field is optional but shall be provided if known. + */ + public String description; // string + /** + * This field shall indicate the audio language for the specific program. The value is a string containing one + * of the standard Tags for Identifying Languages RFC 5646. This field is optional but shall be provided if + * known. + */ + public List audioLanguages; // list + /** + * This field shall be used for indicating the level of parental guidance recommended for of a particular + * program. This can be any rating system used in the country or region where the program is broadcast. For + * example, in the United States “TV-PG” may contain material that parents can find not suitable for younger + * children but can be accepted in general for older children. This field is optional but shall be provided if + * known. + */ + public List ratings; // list + /** + * This field shall represent a URL of a thumbnail that clients can use to render an image for the program. The + * syntax of this field shall follow the syntax as specified in RFC 1738 and shall use the https scheme. + */ + public String thumbnailUrl; // string + /** + * This field shall represent a URL of a poster that clients can use to render an image for the program on the + * detail view. The syntax of this field shall follow the syntax as specified in RFC 1738 and shall use the + * https scheme. + */ + public String posterArtUrl; // string + /** + * This field shall represent the DVB-I URL associated to the program. The syntax of this field shall follow the + * syntax as specified in RFC 1738 and shall use the https scheme. + */ + public String dvbiUrl; // string + /** + * This field shall be a string, in ISO 8601 format, representing the date on which the program was released. + * This field is optional but when provided, the year shall be provided as part of the string. + */ + public String releaseDate; // string + /** + * This field shall represent a string providing additional information on the parental guidance. This field is + * optional. + */ + public String parentalGuidanceText; // string + /** + * This field shall represent the recording status of the program. This field is required if the RecordProgram + * feature is set. + */ + public RecordingFlagBitmap recordingFlag; // RecordingFlagBitmap + /** + * This field shall represent the information of a series such as season and episode number. This field is + * optional but SHOULD be provided if the program represents a series and this information is available. + */ + public SeriesInfoStruct seriesInfo; // SeriesInfoStruct + /** + * This field shall represent the category of a particular program. This field is optional but shall be provided + * if known. + */ + public List categoryList; // list + /** + * This field shall represent a list of the cast or the crew on the program. A single cast member may have more + * than one role. This field is optional but shall be provided if known. + */ + public List castList; // list + /** + * This field shall indicate the list of additional external content identifiers. + */ + public List externalIdList; // list + + public ProgramStruct(String identifier, ChannelInfoStruct channel, Integer startTime, Integer endTime, + String title, String subtitle, String description, List audioLanguages, List ratings, + String thumbnailUrl, String posterArtUrl, String dvbiUrl, String releaseDate, + String parentalGuidanceText, RecordingFlagBitmap recordingFlag, SeriesInfoStruct seriesInfo, + List categoryList, List castList, + List externalIdList) { + this.identifier = identifier; + this.channel = channel; + this.startTime = startTime; + this.endTime = endTime; + this.title = title; + this.subtitle = subtitle; + this.description = description; + this.audioLanguages = audioLanguages; + this.ratings = ratings; + this.thumbnailUrl = thumbnailUrl; + this.posterArtUrl = posterArtUrl; + this.dvbiUrl = dvbiUrl; + this.releaseDate = releaseDate; + this.parentalGuidanceText = parentalGuidanceText; + this.recordingFlag = recordingFlag; + this.seriesInfo = seriesInfo; + this.categoryList = categoryList; + this.castList = castList; + this.externalIdList = externalIdList; + } + } + + /** + * This object defines the category associated to a program. + */ + public class ProgramCategoryStruct { + /** + * This field shall represent the category or genre of the program. Ex. News. + */ + public String category; // string + /** + * This field shall represent the sub-category or sub-genre of the program. Ex. Local. + */ + public String subCategory; // string + + public ProgramCategoryStruct(String category, String subCategory) { + this.category = category; + this.subCategory = subCategory; + } + } + + /** + * This object provides the episode information related to a program. + */ + public class SeriesInfoStruct { + /** + * This field shall represent the season of the series associated to the program. + */ + public String season; // string + /** + * This field shall represent the episode of the program. + */ + public String episode; // string + + public SeriesInfoStruct(String season, String episode) { + this.season = season; + this.episode = episode; + } + } + + /** + * This object provides the cast information related to a program. + */ + public class ProgramCastStruct { + /** + * This field shall represent the name of the cast member. + */ + public String name; // string + /** + * This field shall represent the role of the cast member. Ex. Actor, Director. + */ + public String role; // string + + public ProgramCastStruct(String name, String role) { + this.name = name; + this.role = role; + } + } + + /** + * This object defines the pagination structure. + */ + public class PageTokenStruct { + /** + * This field shall indicate the maximum number of entries that should be retrieved from the program guide in a + * single response. It allows clients to specify the size of the paginated result set based on their needs. + */ + public Integer limit; // uint16 + /** + * This field shall indicate the cursor that pinpoints the start of the upcoming data page. In a Cursor-based + * pagination system, the field acts as a reference point, ensuring the set of results corresponds directly to + * the data following the specified cursor. In a Offset-based pagination system, the field, along with limit, + * indicate the offset from which entries in the program guide will be retrieved. + */ + public String after; // string + /** + * This field shall indicate the cursor that pinpoints the end of the upcoming data page. In a Cursor-based + * pagination system, the field acts as a reference point, ensuring the set of results corresponds directly to + * the data preceding the specified cursor. In a Offset-based pagination system, the field, along with limit, + * indicate the offset from which entries in the program guide will be retrieved. + */ + public String before; // string + + public PageTokenStruct(Integer limit, String after, String before) { + this.limit = limit; + this.after = after; + this.before = before; + } + } + + /** + * This object defines the paging structure that includes the previous and next pagination tokens. + */ + public class ChannelPagingStruct { + /** + * This field shall indicate the token to retrieve the preceding page. Absence of this field denotes the + * response as the initial page. + */ + public PageTokenStruct previousToken; // PageTokenStruct + /** + * This field shall indicate the token to retrieve the next page. Absence of this field denotes the response as + * the last page. + */ + public PageTokenStruct nextToken; // PageTokenStruct + + public ChannelPagingStruct(PageTokenStruct previousToken, PageTokenStruct nextToken) { + this.previousToken = previousToken; + this.nextToken = nextToken; + } + } + + // Enums + public enum LineupInfoTypeEnum implements MatterEnum { + MSO(0, "Mso"); + + public final Integer value; + public final String label; + + private LineupInfoTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + MULTIPLE_MATCHES(1, "Multiple Matches"), + NO_MATCHES(2, "No Matches"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ChannelTypeEnum implements MatterEnum { + SATELLITE(0, "Satellite"), + CABLE(1, "Cable"), + TERRESTRIAL(2, "Terrestrial"), + OTT(3, "Ott"); + + public final Integer value; + public final String label; + + private ChannelTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class RecordingFlagBitmap { + public boolean scheduled; + public boolean recordSeries; + public boolean recorded; + + public RecordingFlagBitmap(boolean scheduled, boolean recordSeries, boolean recorded) { + this.scheduled = scheduled; + this.recordSeries = recordSeries; + this.recorded = recorded; + } + } + + public static class FeatureMap { + /** + * + * Provides list of available channels. + */ + public boolean channelList; + /** + * + * Provides lineup info, which is a reference to an external source of lineup information. + */ + public boolean lineupInfo; + /** + * + * Provides electronic program guide information. + */ + public boolean electronicGuide; + /** + * + * Provides ability to record program. + */ + public boolean recordProgram; + + public FeatureMap(boolean channelList, boolean lineupInfo, boolean electronicGuide, boolean recordProgram) { + this.channelList = channelList; + this.lineupInfo = lineupInfo; + this.electronicGuide = electronicGuide; + this.recordProgram = recordProgram; + } + } + + public ChannelCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1284, "Channel"); + } + + protected ChannelCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Change the channel to the channel case-insensitive exact matching the value passed as an argument. + * The match priority order shall be: Identifier, AffiliateCallSign, CallSign, Name, Number. In the match string, + * the Channel number should be presented in the "Major.Minor" format, such as "13.1". + * Upon receipt, this shall generate a ChangeChannelResponse command. + * Upon success, the CurrentChannel attribute, if supported, shall be updated to reflect the change. + */ + public static ClusterCommand changeChannel(String match) { + Map map = new LinkedHashMap<>(); + if (match != null) { + map.put("match", match); + } + return new ClusterCommand("changeChannel", map); + } + + /** + * Change the channel to the channel with the given Number in the ChannelList attribute. + */ + public static ClusterCommand changeChannelByNumber(Integer majorNumber, Integer minorNumber) { + Map map = new LinkedHashMap<>(); + if (majorNumber != null) { + map.put("majorNumber", majorNumber); + } + if (minorNumber != null) { + map.put("minorNumber", minorNumber); + } + return new ClusterCommand("changeChannelByNumber", map); + } + + /** + * This command provides channel up and channel down functionality, but allows channel index jumps of size Count. + * When the value of the increase or decrease is larger than the number of channels remaining in the given + * direction, then the behavior shall be to return to the beginning (or end) of the channel list and continue. For + * example, if the current channel is at index 0 and count value of -1 is given, then the current channel should + * change to the last channel. + */ + public static ClusterCommand skipChannel(Integer count) { + Map map = new LinkedHashMap<>(); + if (count != null) { + map.put("count", count); + } + return new ClusterCommand("skipChannel", map); + } + + /** + * This command retrieves the program guide. It accepts several filter parameters to return specific schedule and + * program information from a content app. The command shall receive in response a ProgramGuideResponse. Standard + * error codes shall be used when arguments provided are not valid. For example, if StartTime is greater than + * EndTime, the status code INVALID_ACTION shall be returned. + */ + public static ClusterCommand getProgramGuide(Integer startTime, Integer endTime, + List channelList, PageTokenStruct pageToken, RecordingFlagBitmap recordingFlag, + List externalIdList, OctetString data) { + Map map = new LinkedHashMap<>(); + if (startTime != null) { + map.put("startTime", startTime); + } + if (endTime != null) { + map.put("endTime", endTime); + } + if (channelList != null) { + map.put("channelList", channelList); + } + if (pageToken != null) { + map.put("pageToken", pageToken); + } + if (recordingFlag != null) { + map.put("recordingFlag", recordingFlag); + } + if (externalIdList != null) { + map.put("externalIdList", externalIdList); + } + if (data != null) { + map.put("data", data); + } + return new ClusterCommand("getProgramGuide", map); + } + + /** + * Record a specific program or series when it goes live. This functionality enables DVR recording features. + */ + public static ClusterCommand recordProgram(String programIdentifier, Boolean shouldRecordSeries, + List externalIdList, OctetString data) { + Map map = new LinkedHashMap<>(); + if (programIdentifier != null) { + map.put("programIdentifier", programIdentifier); + } + if (shouldRecordSeries != null) { + map.put("shouldRecordSeries", shouldRecordSeries); + } + if (externalIdList != null) { + map.put("externalIdList", externalIdList); + } + if (data != null) { + map.put("data", data); + } + return new ClusterCommand("recordProgram", map); + } + + /** + * Cancel recording for a specific program or series. + */ + public static ClusterCommand cancelRecordProgram(String programIdentifier, Boolean shouldRecordSeries, + List externalIdList, OctetString data) { + Map map = new LinkedHashMap<>(); + if (programIdentifier != null) { + map.put("programIdentifier", programIdentifier); + } + if (shouldRecordSeries != null) { + map.put("shouldRecordSeries", shouldRecordSeries); + } + if (externalIdList != null) { + map.put("externalIdList", externalIdList); + } + if (data != null) { + map.put("data", data); + } + return new ClusterCommand("cancelRecordProgram", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "channelList : " + channelList + "\n"; + str += "lineup : " + lineup + "\n"; + str += "currentChannel : " + currentChannel + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ClusterConstants.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ClusterConstants.java new file mode 100644 index 00000000000..336abbda145 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ClusterConstants.java @@ -0,0 +1,4810 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import org.openhab.core.thing.type.ChannelTypeUID; + +/** + * + * ClusterThingTypes + * + * @author Dan Cunningham - Initial contribution + */ +public class ClusterConstants { + + // AccessControl Cluster + public static final String CHANNEL_NAME_ACCESSCONTROL_ACL = "Acl"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_ACL = "Acl"; + public static final String CHANNEL_ID_ACCESSCONTROL_ACL = "accesscontrol-acl"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_ACL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_ACL); + + public static final String CHANNEL_NAME_ACCESSCONTROL_EXTENSION = "Extension"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_EXTENSION = "Extension"; + public static final String CHANNEL_ID_ACCESSCONTROL_EXTENSION = "accesscontrol-extension"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_EXTENSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_EXTENSION); + + public static final String CHANNEL_NAME_ACCESSCONTROL_SUBJECTSPERACCESSCONTROLENTRY = "SubjectsPerAccessControlEntry"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_SUBJECTSPERACCESSCONTROLENTRY = "Subjects Per Access Control Entry"; + public static final String CHANNEL_ID_ACCESSCONTROL_SUBJECTSPERACCESSCONTROLENTRY = "accesscontrol-subjectsperaccesscontrolentry"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_SUBJECTSPERACCESSCONTROLENTRY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_SUBJECTSPERACCESSCONTROLENTRY); + + public static final String CHANNEL_NAME_ACCESSCONTROL_TARGETSPERACCESSCONTROLENTRY = "TargetsPerAccessControlEntry"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_TARGETSPERACCESSCONTROLENTRY = "Targets Per Access Control Entry"; + public static final String CHANNEL_ID_ACCESSCONTROL_TARGETSPERACCESSCONTROLENTRY = "accesscontrol-targetsperaccesscontrolentry"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_TARGETSPERACCESSCONTROLENTRY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_TARGETSPERACCESSCONTROLENTRY); + + public static final String CHANNEL_NAME_ACCESSCONTROL_ACCESSCONTROLENTRIESPERFABRIC = "AccessControlEntriesPerFabric"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_ACCESSCONTROLENTRIESPERFABRIC = "Access Control Entries Per Fabric"; + public static final String CHANNEL_ID_ACCESSCONTROL_ACCESSCONTROLENTRIESPERFABRIC = "accesscontrol-accesscontrolentriesperfabric"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_ACCESSCONTROLENTRIESPERFABRIC = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_ACCESSCONTROLENTRIESPERFABRIC); + + public static final String CHANNEL_NAME_ACCESSCONTROL_COMMISSIONINGARL = "CommissioningArl"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_COMMISSIONINGARL = "Commissioning Arl"; + public static final String CHANNEL_ID_ACCESSCONTROL_COMMISSIONINGARL = "accesscontrol-commissioningarl"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_COMMISSIONINGARL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_COMMISSIONINGARL); + + public static final String CHANNEL_NAME_ACCESSCONTROL_ARL = "Arl"; + public static final String CHANNEL_LABEL_ACCESSCONTROL_ARL = "Arl"; + public static final String CHANNEL_ID_ACCESSCONTROL_ARL = "accesscontrol-arl"; + public static final ChannelTypeUID CHANNEL_ACCESSCONTROL_ARL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACCESSCONTROL_ARL); + + // AccountLogin Cluster + // Actions Cluster + public static final String CHANNEL_NAME_ACTIONS_ACTIONLIST = "ActionList"; + public static final String CHANNEL_LABEL_ACTIONS_ACTIONLIST = "Action List"; + public static final String CHANNEL_ID_ACTIONS_ACTIONLIST = "actions-actionlist"; + public static final ChannelTypeUID CHANNEL_ACTIONS_ACTIONLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACTIONS_ACTIONLIST); + + public static final String CHANNEL_NAME_ACTIONS_ENDPOINTLISTS = "EndpointLists"; + public static final String CHANNEL_LABEL_ACTIONS_ENDPOINTLISTS = "Endpoint Lists"; + public static final String CHANNEL_ID_ACTIONS_ENDPOINTLISTS = "actions-endpointlists"; + public static final ChannelTypeUID CHANNEL_ACTIONS_ENDPOINTLISTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACTIONS_ENDPOINTLISTS); + + public static final String CHANNEL_NAME_ACTIONS_SETUPURL = "SetupUrl"; + public static final String CHANNEL_LABEL_ACTIONS_SETUPURL = "Setup Url"; + public static final String CHANNEL_ID_ACTIONS_SETUPURL = "actions-setupurl"; + public static final ChannelTypeUID CHANNEL_ACTIONS_SETUPURL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ACTIONS_SETUPURL); + + // ActivatedCarbonFilterMonitoring Cluster + // AdministratorCommissioning Cluster + public static final String CHANNEL_NAME_ADMINISTRATORCOMMISSIONING_WINDOWSTATUS = "WindowStatus"; + public static final String CHANNEL_LABEL_ADMINISTRATORCOMMISSIONING_WINDOWSTATUS = "Window Status"; + public static final String CHANNEL_ID_ADMINISTRATORCOMMISSIONING_WINDOWSTATUS = "administratorcommissioning-windowstatus"; + public static final ChannelTypeUID CHANNEL_ADMINISTRATORCOMMISSIONING_WINDOWSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ADMINISTRATORCOMMISSIONING_WINDOWSTATUS); + + public static final String CHANNEL_NAME_ADMINISTRATORCOMMISSIONING_ADMINFABRICINDEX = "AdminFabricIndex"; + public static final String CHANNEL_LABEL_ADMINISTRATORCOMMISSIONING_ADMINFABRICINDEX = "Admin Fabric Index"; + public static final String CHANNEL_ID_ADMINISTRATORCOMMISSIONING_ADMINFABRICINDEX = "administratorcommissioning-adminfabricindex"; + public static final ChannelTypeUID CHANNEL_ADMINISTRATORCOMMISSIONING_ADMINFABRICINDEX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ADMINISTRATORCOMMISSIONING_ADMINFABRICINDEX); + + public static final String CHANNEL_NAME_ADMINISTRATORCOMMISSIONING_ADMINVENDORID = "AdminVendorId"; + public static final String CHANNEL_LABEL_ADMINISTRATORCOMMISSIONING_ADMINVENDORID = "Admin Vendor Id"; + public static final String CHANNEL_ID_ADMINISTRATORCOMMISSIONING_ADMINVENDORID = "administratorcommissioning-adminvendorid"; + public static final ChannelTypeUID CHANNEL_ADMINISTRATORCOMMISSIONING_ADMINVENDORID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ADMINISTRATORCOMMISSIONING_ADMINVENDORID); + + // AirQuality Cluster + public static final String CHANNEL_NAME_AIRQUALITY_AIRQUALITY = "AirQuality"; + public static final String CHANNEL_LABEL_AIRQUALITY_AIRQUALITY = "Air Quality"; + public static final String CHANNEL_ID_AIRQUALITY_AIRQUALITY = "airquality-airquality"; + public static final ChannelTypeUID CHANNEL_AIRQUALITY_AIRQUALITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_AIRQUALITY_AIRQUALITY); + + // AlarmBase Cluster + public static final String CHANNEL_NAME_ALARMBASE_MASK = "Mask"; + public static final String CHANNEL_LABEL_ALARMBASE_MASK = "Mask"; + public static final String CHANNEL_ID_ALARMBASE_MASK = "alarmbase-mask"; + public static final ChannelTypeUID CHANNEL_ALARMBASE_MASK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ALARMBASE_MASK); + + public static final String CHANNEL_NAME_ALARMBASE_LATCH = "Latch"; + public static final String CHANNEL_LABEL_ALARMBASE_LATCH = "Latch"; + public static final String CHANNEL_ID_ALARMBASE_LATCH = "alarmbase-latch"; + public static final ChannelTypeUID CHANNEL_ALARMBASE_LATCH = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ALARMBASE_LATCH); + + public static final String CHANNEL_NAME_ALARMBASE_STATE = "State"; + public static final String CHANNEL_LABEL_ALARMBASE_STATE = "State"; + public static final String CHANNEL_ID_ALARMBASE_STATE = "alarmbase-state"; + public static final ChannelTypeUID CHANNEL_ALARMBASE_STATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ALARMBASE_STATE); + + public static final String CHANNEL_NAME_ALARMBASE_SUPPORTED = "Supported"; + public static final String CHANNEL_LABEL_ALARMBASE_SUPPORTED = "Supported"; + public static final String CHANNEL_ID_ALARMBASE_SUPPORTED = "alarmbase-supported"; + public static final ChannelTypeUID CHANNEL_ALARMBASE_SUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ALARMBASE_SUPPORTED); + + // ApplicationBasic Cluster + public static final String CHANNEL_NAME_APPLICATIONBASIC_VENDORNAME = "VendorName"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_VENDORNAME = "Vendor Name"; + public static final String CHANNEL_ID_APPLICATIONBASIC_VENDORNAME = "applicationbasic-vendorname"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_VENDORNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_VENDORNAME); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_VENDORID = "VendorId"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_VENDORID = "Vendor Id"; + public static final String CHANNEL_ID_APPLICATIONBASIC_VENDORID = "applicationbasic-vendorid"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_VENDORID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_VENDORID); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_APPLICATIONNAME = "ApplicationName"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_APPLICATIONNAME = "Application Name"; + public static final String CHANNEL_ID_APPLICATIONBASIC_APPLICATIONNAME = "applicationbasic-applicationname"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_APPLICATIONNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_APPLICATIONNAME); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_PRODUCTID = "ProductId"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_PRODUCTID = "Product Id"; + public static final String CHANNEL_ID_APPLICATIONBASIC_PRODUCTID = "applicationbasic-productid"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_PRODUCTID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_PRODUCTID); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_APPLICATION = "Application"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_APPLICATION = "Application"; + public static final String CHANNEL_ID_APPLICATIONBASIC_APPLICATION = "applicationbasic-application"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_APPLICATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_APPLICATION); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_STATUS = "Status"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_STATUS = "Status"; + public static final String CHANNEL_ID_APPLICATIONBASIC_STATUS = "applicationbasic-status"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_STATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_STATUS); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_APPLICATIONVERSION = "ApplicationVersion"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_APPLICATIONVERSION = "Application Version"; + public static final String CHANNEL_ID_APPLICATIONBASIC_APPLICATIONVERSION = "applicationbasic-applicationversion"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_APPLICATIONVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_APPLICATIONVERSION); + + public static final String CHANNEL_NAME_APPLICATIONBASIC_ALLOWEDVENDORLIST = "AllowedVendorList"; + public static final String CHANNEL_LABEL_APPLICATIONBASIC_ALLOWEDVENDORLIST = "Allowed Vendor List"; + public static final String CHANNEL_ID_APPLICATIONBASIC_ALLOWEDVENDORLIST = "applicationbasic-allowedvendorlist"; + public static final ChannelTypeUID CHANNEL_APPLICATIONBASIC_ALLOWEDVENDORLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONBASIC_ALLOWEDVENDORLIST); + + // ApplicationLauncher Cluster + public static final String CHANNEL_NAME_APPLICATIONLAUNCHER_CATALOGLIST = "CatalogList"; + public static final String CHANNEL_LABEL_APPLICATIONLAUNCHER_CATALOGLIST = "Catalog List"; + public static final String CHANNEL_ID_APPLICATIONLAUNCHER_CATALOGLIST = "applicationlauncher-cataloglist"; + public static final ChannelTypeUID CHANNEL_APPLICATIONLAUNCHER_CATALOGLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONLAUNCHER_CATALOGLIST); + + public static final String CHANNEL_NAME_APPLICATIONLAUNCHER_CURRENTAPP = "CurrentApp"; + public static final String CHANNEL_LABEL_APPLICATIONLAUNCHER_CURRENTAPP = "Current App"; + public static final String CHANNEL_ID_APPLICATIONLAUNCHER_CURRENTAPP = "applicationlauncher-currentapp"; + public static final ChannelTypeUID CHANNEL_APPLICATIONLAUNCHER_CURRENTAPP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_APPLICATIONLAUNCHER_CURRENTAPP); + + // AudioOutput Cluster + public static final String CHANNEL_NAME_AUDIOOUTPUT_OUTPUTLIST = "OutputList"; + public static final String CHANNEL_LABEL_AUDIOOUTPUT_OUTPUTLIST = "Output List"; + public static final String CHANNEL_ID_AUDIOOUTPUT_OUTPUTLIST = "audiooutput-outputlist"; + public static final ChannelTypeUID CHANNEL_AUDIOOUTPUT_OUTPUTLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_AUDIOOUTPUT_OUTPUTLIST); + + public static final String CHANNEL_NAME_AUDIOOUTPUT_CURRENTOUTPUT = "CurrentOutput"; + public static final String CHANNEL_LABEL_AUDIOOUTPUT_CURRENTOUTPUT = "Current Output"; + public static final String CHANNEL_ID_AUDIOOUTPUT_CURRENTOUTPUT = "audiooutput-currentoutput"; + public static final ChannelTypeUID CHANNEL_AUDIOOUTPUT_CURRENTOUTPUT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_AUDIOOUTPUT_CURRENTOUTPUT); + + // BallastConfiguration Cluster + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_PHYSICALMINLEVEL = "PhysicalMinLevel"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_PHYSICALMINLEVEL = "Physical Min Level"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_PHYSICALMINLEVEL = "ballastconfiguration-physicalminlevel"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_PHYSICALMINLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_PHYSICALMINLEVEL); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_PHYSICALMAXLEVEL = "PhysicalMaxLevel"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_PHYSICALMAXLEVEL = "Physical Max Level"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_PHYSICALMAXLEVEL = "ballastconfiguration-physicalmaxlevel"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_PHYSICALMAXLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_PHYSICALMAXLEVEL); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_BALLASTSTATUS = "BallastStatus"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_BALLASTSTATUS = "Ballast Status"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_BALLASTSTATUS = "ballastconfiguration-ballaststatus"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_BALLASTSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_BALLASTSTATUS); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_MINLEVEL = "MinLevel"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_MINLEVEL = "Min Level"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_MINLEVEL = "ballastconfiguration-minlevel"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_MINLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_MINLEVEL); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_MAXLEVEL = "MaxLevel"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_MAXLEVEL = "Max Level"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_MAXLEVEL = "ballastconfiguration-maxlevel"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_MAXLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_MAXLEVEL); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_INTRINSICBALLASTFACTOR = "IntrinsicBallastFactor"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_INTRINSICBALLASTFACTOR = "Intrinsic Ballast Factor"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_INTRINSICBALLASTFACTOR = "ballastconfiguration-intrinsicballastfactor"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_INTRINSICBALLASTFACTOR = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_INTRINSICBALLASTFACTOR); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_BALLASTFACTORADJUSTMENT = "BallastFactorAdjustment"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_BALLASTFACTORADJUSTMENT = "Ballast Factor Adjustment"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_BALLASTFACTORADJUSTMENT = "ballastconfiguration-ballastfactoradjustment"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_BALLASTFACTORADJUSTMENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_BALLASTFACTORADJUSTMENT); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPQUANTITY = "LampQuantity"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPQUANTITY = "Lamp Quantity"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPQUANTITY = "ballastconfiguration-lampquantity"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPQUANTITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPQUANTITY); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPTYPE = "LampType"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPTYPE = "Lamp Type"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPTYPE = "ballastconfiguration-lamptype"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPTYPE); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPMANUFACTURER = "LampManufacturer"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPMANUFACTURER = "Lamp Manufacturer"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPMANUFACTURER = "ballastconfiguration-lampmanufacturer"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPMANUFACTURER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPMANUFACTURER); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPRATEDHOURS = "LampRatedHours"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPRATEDHOURS = "Lamp Rated Hours"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPRATEDHOURS = "ballastconfiguration-lampratedhours"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPRATEDHOURS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPRATEDHOURS); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPBURNHOURS = "LampBurnHours"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPBURNHOURS = "Lamp Burn Hours"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPBURNHOURS = "ballastconfiguration-lampburnhours"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPBURNHOURS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPBURNHOURS); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPALARMMODE = "LampAlarmMode"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPALARMMODE = "Lamp Alarm Mode"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPALARMMODE = "ballastconfiguration-lampalarmmode"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPALARMMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPALARMMODE); + + public static final String CHANNEL_NAME_BALLASTCONFIGURATION_LAMPBURNHOURSTRIPPOINT = "LampBurnHoursTripPoint"; + public static final String CHANNEL_LABEL_BALLASTCONFIGURATION_LAMPBURNHOURSTRIPPOINT = "Lamp Burn Hours Trip Point"; + public static final String CHANNEL_ID_BALLASTCONFIGURATION_LAMPBURNHOURSTRIPPOINT = "ballastconfiguration-lampburnhourstrippoint"; + public static final ChannelTypeUID CHANNEL_BALLASTCONFIGURATION_LAMPBURNHOURSTRIPPOINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BALLASTCONFIGURATION_LAMPBURNHOURSTRIPPOINT); + + // BasicInformation Cluster + public static final String CHANNEL_NAME_BASICINFORMATION_DATAMODELREVISION = "DataModelRevision"; + public static final String CHANNEL_LABEL_BASICINFORMATION_DATAMODELREVISION = "Data Model Revision"; + public static final String CHANNEL_ID_BASICINFORMATION_DATAMODELREVISION = "basicinformation-datamodelrevision"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_DATAMODELREVISION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_DATAMODELREVISION); + + public static final String CHANNEL_NAME_BASICINFORMATION_VENDORNAME = "VendorName"; + public static final String CHANNEL_LABEL_BASICINFORMATION_VENDORNAME = "Vendor Name"; + public static final String CHANNEL_ID_BASICINFORMATION_VENDORNAME = "basicinformation-vendorname"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_VENDORNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_VENDORNAME); + + public static final String CHANNEL_NAME_BASICINFORMATION_VENDORID = "VendorId"; + public static final String CHANNEL_LABEL_BASICINFORMATION_VENDORID = "Vendor Id"; + public static final String CHANNEL_ID_BASICINFORMATION_VENDORID = "basicinformation-vendorid"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_VENDORID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_VENDORID); + + public static final String CHANNEL_NAME_BASICINFORMATION_PRODUCTNAME = "ProductName"; + public static final String CHANNEL_LABEL_BASICINFORMATION_PRODUCTNAME = "Product Name"; + public static final String CHANNEL_ID_BASICINFORMATION_PRODUCTNAME = "basicinformation-productname"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_PRODUCTNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_PRODUCTNAME); + + public static final String CHANNEL_NAME_BASICINFORMATION_PRODUCTID = "ProductId"; + public static final String CHANNEL_LABEL_BASICINFORMATION_PRODUCTID = "Product Id"; + public static final String CHANNEL_ID_BASICINFORMATION_PRODUCTID = "basicinformation-productid"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_PRODUCTID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_PRODUCTID); + + public static final String CHANNEL_NAME_BASICINFORMATION_NODELABEL = "NodeLabel"; + public static final String CHANNEL_LABEL_BASICINFORMATION_NODELABEL = "Node Label"; + public static final String CHANNEL_ID_BASICINFORMATION_NODELABEL = "basicinformation-nodelabel"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_NODELABEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_NODELABEL); + + public static final String CHANNEL_NAME_BASICINFORMATION_LOCATION = "Location"; + public static final String CHANNEL_LABEL_BASICINFORMATION_LOCATION = "Location"; + public static final String CHANNEL_ID_BASICINFORMATION_LOCATION = "basicinformation-location"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_LOCATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_LOCATION); + + public static final String CHANNEL_NAME_BASICINFORMATION_HARDWAREVERSION = "HardwareVersion"; + public static final String CHANNEL_LABEL_BASICINFORMATION_HARDWAREVERSION = "Hardware Version"; + public static final String CHANNEL_ID_BASICINFORMATION_HARDWAREVERSION = "basicinformation-hardwareversion"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_HARDWAREVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_HARDWAREVERSION); + + public static final String CHANNEL_NAME_BASICINFORMATION_HARDWAREVERSIONSTRING = "HardwareVersionString"; + public static final String CHANNEL_LABEL_BASICINFORMATION_HARDWAREVERSIONSTRING = "Hardware Version String"; + public static final String CHANNEL_ID_BASICINFORMATION_HARDWAREVERSIONSTRING = "basicinformation-hardwareversionstring"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_HARDWAREVERSIONSTRING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_HARDWAREVERSIONSTRING); + + public static final String CHANNEL_NAME_BASICINFORMATION_SOFTWAREVERSION = "SoftwareVersion"; + public static final String CHANNEL_LABEL_BASICINFORMATION_SOFTWAREVERSION = "Software Version"; + public static final String CHANNEL_ID_BASICINFORMATION_SOFTWAREVERSION = "basicinformation-softwareversion"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_SOFTWAREVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_SOFTWAREVERSION); + + public static final String CHANNEL_NAME_BASICINFORMATION_SOFTWAREVERSIONSTRING = "SoftwareVersionString"; + public static final String CHANNEL_LABEL_BASICINFORMATION_SOFTWAREVERSIONSTRING = "Software Version String"; + public static final String CHANNEL_ID_BASICINFORMATION_SOFTWAREVERSIONSTRING = "basicinformation-softwareversionstring"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_SOFTWAREVERSIONSTRING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_SOFTWAREVERSIONSTRING); + + public static final String CHANNEL_NAME_BASICINFORMATION_MANUFACTURINGDATE = "ManufacturingDate"; + public static final String CHANNEL_LABEL_BASICINFORMATION_MANUFACTURINGDATE = "Manufacturing Date"; + public static final String CHANNEL_ID_BASICINFORMATION_MANUFACTURINGDATE = "basicinformation-manufacturingdate"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_MANUFACTURINGDATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_MANUFACTURINGDATE); + + public static final String CHANNEL_NAME_BASICINFORMATION_PARTNUMBER = "PartNumber"; + public static final String CHANNEL_LABEL_BASICINFORMATION_PARTNUMBER = "Part Number"; + public static final String CHANNEL_ID_BASICINFORMATION_PARTNUMBER = "basicinformation-partnumber"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_PARTNUMBER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_PARTNUMBER); + + public static final String CHANNEL_NAME_BASICINFORMATION_PRODUCTURL = "ProductUrl"; + public static final String CHANNEL_LABEL_BASICINFORMATION_PRODUCTURL = "Product Url"; + public static final String CHANNEL_ID_BASICINFORMATION_PRODUCTURL = "basicinformation-producturl"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_PRODUCTURL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_PRODUCTURL); + + public static final String CHANNEL_NAME_BASICINFORMATION_PRODUCTLABEL = "ProductLabel"; + public static final String CHANNEL_LABEL_BASICINFORMATION_PRODUCTLABEL = "Product Label"; + public static final String CHANNEL_ID_BASICINFORMATION_PRODUCTLABEL = "basicinformation-productlabel"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_PRODUCTLABEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_PRODUCTLABEL); + + public static final String CHANNEL_NAME_BASICINFORMATION_SERIALNUMBER = "SerialNumber"; + public static final String CHANNEL_LABEL_BASICINFORMATION_SERIALNUMBER = "Serial Number"; + public static final String CHANNEL_ID_BASICINFORMATION_SERIALNUMBER = "basicinformation-serialnumber"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_SERIALNUMBER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_SERIALNUMBER); + + public static final String CHANNEL_NAME_BASICINFORMATION_LOCALCONFIGDISABLED = "LocalConfigDisabled"; + public static final String CHANNEL_LABEL_BASICINFORMATION_LOCALCONFIGDISABLED = "Local Config Disabled"; + public static final String CHANNEL_ID_BASICINFORMATION_LOCALCONFIGDISABLED = "basicinformation-localconfigdisabled"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_LOCALCONFIGDISABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_LOCALCONFIGDISABLED); + + public static final String CHANNEL_NAME_BASICINFORMATION_REACHABLE = "Reachable"; + public static final String CHANNEL_LABEL_BASICINFORMATION_REACHABLE = "Reachable"; + public static final String CHANNEL_ID_BASICINFORMATION_REACHABLE = "basicinformation-reachable"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_REACHABLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_REACHABLE); + + public static final String CHANNEL_NAME_BASICINFORMATION_UNIQUEID = "UniqueId"; + public static final String CHANNEL_LABEL_BASICINFORMATION_UNIQUEID = "Unique Id"; + public static final String CHANNEL_ID_BASICINFORMATION_UNIQUEID = "basicinformation-uniqueid"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_UNIQUEID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_UNIQUEID); + + public static final String CHANNEL_NAME_BASICINFORMATION_CAPABILITYMINIMA = "CapabilityMinima"; + public static final String CHANNEL_LABEL_BASICINFORMATION_CAPABILITYMINIMA = "Capability Minima"; + public static final String CHANNEL_ID_BASICINFORMATION_CAPABILITYMINIMA = "basicinformation-capabilityminima"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_CAPABILITYMINIMA = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_CAPABILITYMINIMA); + + public static final String CHANNEL_NAME_BASICINFORMATION_PRODUCTAPPEARANCE = "ProductAppearance"; + public static final String CHANNEL_LABEL_BASICINFORMATION_PRODUCTAPPEARANCE = "Product Appearance"; + public static final String CHANNEL_ID_BASICINFORMATION_PRODUCTAPPEARANCE = "basicinformation-productappearance"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_PRODUCTAPPEARANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_PRODUCTAPPEARANCE); + + public static final String CHANNEL_NAME_BASICINFORMATION_SPECIFICATIONVERSION = "SpecificationVersion"; + public static final String CHANNEL_LABEL_BASICINFORMATION_SPECIFICATIONVERSION = "Specification Version"; + public static final String CHANNEL_ID_BASICINFORMATION_SPECIFICATIONVERSION = "basicinformation-specificationversion"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_SPECIFICATIONVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_SPECIFICATIONVERSION); + + public static final String CHANNEL_NAME_BASICINFORMATION_MAXPATHSPERINVOKE = "MaxPathsPerInvoke"; + public static final String CHANNEL_LABEL_BASICINFORMATION_MAXPATHSPERINVOKE = "Max Paths Per Invoke"; + public static final String CHANNEL_ID_BASICINFORMATION_MAXPATHSPERINVOKE = "basicinformation-maxpathsperinvoke"; + public static final ChannelTypeUID CHANNEL_BASICINFORMATION_MAXPATHSPERINVOKE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BASICINFORMATION_MAXPATHSPERINVOKE); + + // Binding Cluster + public static final String CHANNEL_NAME_BINDING_BINDING = "Binding"; + public static final String CHANNEL_LABEL_BINDING_BINDING = "Binding"; + public static final String CHANNEL_ID_BINDING_BINDING = "binding-binding"; + public static final ChannelTypeUID CHANNEL_BINDING_BINDING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BINDING_BINDING); + + // BooleanState Cluster + public static final String CHANNEL_NAME_BOOLEANSTATE_STATEVALUE = "StateValue"; + public static final String CHANNEL_LABEL_BOOLEANSTATE_STATEVALUE = "State Value"; + public static final String CHANNEL_ID_BOOLEANSTATE_STATEVALUE = "booleanstate-statevalue"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATE_STATEVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATE_STATEVALUE); + + // BooleanStateConfiguration Cluster + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_CURRENTSENSITIVITYLEVEL = "CurrentSensitivityLevel"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_CURRENTSENSITIVITYLEVEL = "Current Sensitivity Level"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_CURRENTSENSITIVITYLEVEL = "booleanstateconfiguration-currentsensitivitylevel"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_CURRENTSENSITIVITYLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_CURRENTSENSITIVITYLEVEL); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_SUPPORTEDSENSITIVITYLEVELS = "SupportedSensitivityLevels"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_SUPPORTEDSENSITIVITYLEVELS = "Supported Sensitivity Levels"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_SUPPORTEDSENSITIVITYLEVELS = "booleanstateconfiguration-supportedsensitivitylevels"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_SUPPORTEDSENSITIVITYLEVELS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_SUPPORTEDSENSITIVITYLEVELS); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_DEFAULTSENSITIVITYLEVEL = "DefaultSensitivityLevel"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_DEFAULTSENSITIVITYLEVEL = "Default Sensitivity Level"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_DEFAULTSENSITIVITYLEVEL = "booleanstateconfiguration-defaultsensitivitylevel"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_DEFAULTSENSITIVITYLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_DEFAULTSENSITIVITYLEVEL); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_ALARMSACTIVE = "AlarmsActive"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_ALARMSACTIVE = "Alarms Active"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSACTIVE = "booleanstateconfiguration-alarmsactive"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_ALARMSACTIVE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSACTIVE); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_ALARMSSUPPRESSED = "AlarmsSuppressed"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_ALARMSSUPPRESSED = "Alarms Suppressed"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSSUPPRESSED = "booleanstateconfiguration-alarmssuppressed"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_ALARMSSUPPRESSED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSSUPPRESSED); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_ALARMSENABLED = "AlarmsEnabled"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_ALARMSENABLED = "Alarms Enabled"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSENABLED = "booleanstateconfiguration-alarmsenabled"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_ALARMSENABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSENABLED); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_ALARMSSUPPORTED = "AlarmsSupported"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_ALARMSSUPPORTED = "Alarms Supported"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSSUPPORTED = "booleanstateconfiguration-alarmssupported"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_ALARMSSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_ALARMSSUPPORTED); + + public static final String CHANNEL_NAME_BOOLEANSTATECONFIGURATION_SENSORFAULT = "SensorFault"; + public static final String CHANNEL_LABEL_BOOLEANSTATECONFIGURATION_SENSORFAULT = "Sensor Fault"; + public static final String CHANNEL_ID_BOOLEANSTATECONFIGURATION_SENSORFAULT = "booleanstateconfiguration-sensorfault"; + public static final ChannelTypeUID CHANNEL_BOOLEANSTATECONFIGURATION_SENSORFAULT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_BOOLEANSTATECONFIGURATION_SENSORFAULT); + + // BridgedDeviceBasicInformation Cluster + // CarbonDioxideConcentrationMeasurement Cluster + // CarbonMonoxideConcentrationMeasurement Cluster + // Channel Cluster + public static final String CHANNEL_NAME_CHANNEL_CHANNELLIST = "ChannelList"; + public static final String CHANNEL_LABEL_CHANNEL_CHANNELLIST = "Channel List"; + public static final String CHANNEL_ID_CHANNEL_CHANNELLIST = "channel-channellist"; + public static final ChannelTypeUID CHANNEL_CHANNEL_CHANNELLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CHANNEL_CHANNELLIST); + + public static final String CHANNEL_NAME_CHANNEL_LINEUP = "Lineup"; + public static final String CHANNEL_LABEL_CHANNEL_LINEUP = "Lineup"; + public static final String CHANNEL_ID_CHANNEL_LINEUP = "channel-lineup"; + public static final ChannelTypeUID CHANNEL_CHANNEL_LINEUP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CHANNEL_LINEUP); + + public static final String CHANNEL_NAME_CHANNEL_CURRENTCHANNEL = "CurrentChannel"; + public static final String CHANNEL_LABEL_CHANNEL_CURRENTCHANNEL = "Current Channel"; + public static final String CHANNEL_ID_CHANNEL_CURRENTCHANNEL = "channel-currentchannel"; + public static final ChannelTypeUID CHANNEL_CHANNEL_CURRENTCHANNEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CHANNEL_CURRENTCHANNEL); + + // ColorControl Cluster + public static final String CHANNEL_NAME_COLORCONTROL_CURRENTHUE = "CurrentHue"; + public static final String CHANNEL_LABEL_COLORCONTROL_CURRENTHUE = "Current Hue"; + public static final String CHANNEL_ID_COLORCONTROL_CURRENTHUE = "colorcontrol-currenthue"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_CURRENTHUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_CURRENTHUE); + + public static final String CHANNEL_NAME_COLORCONTROL_CURRENTSATURATION = "CurrentSaturation"; + public static final String CHANNEL_LABEL_COLORCONTROL_CURRENTSATURATION = "Current Saturation"; + public static final String CHANNEL_ID_COLORCONTROL_CURRENTSATURATION = "colorcontrol-currentsaturation"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_CURRENTSATURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_CURRENTSATURATION); + + public static final String CHANNEL_NAME_COLORCONTROL_REMAININGTIME = "RemainingTime"; + public static final String CHANNEL_LABEL_COLORCONTROL_REMAININGTIME = "Remaining Time"; + public static final String CHANNEL_ID_COLORCONTROL_REMAININGTIME = "colorcontrol-remainingtime"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_REMAININGTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_REMAININGTIME); + + public static final String CHANNEL_NAME_COLORCONTROL_CURRENTX = "CurrentX"; + public static final String CHANNEL_LABEL_COLORCONTROL_CURRENTX = "Current X"; + public static final String CHANNEL_ID_COLORCONTROL_CURRENTX = "colorcontrol-currentx"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_CURRENTX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_CURRENTX); + + public static final String CHANNEL_NAME_COLORCONTROL_CURRENTY = "CurrentY"; + public static final String CHANNEL_LABEL_COLORCONTROL_CURRENTY = "Current Y"; + public static final String CHANNEL_ID_COLORCONTROL_CURRENTY = "colorcontrol-currenty"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_CURRENTY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_CURRENTY); + + public static final String CHANNEL_NAME_COLORCONTROL_DRIFTCOMPENSATION = "DriftCompensation"; + public static final String CHANNEL_LABEL_COLORCONTROL_DRIFTCOMPENSATION = "Drift Compensation"; + public static final String CHANNEL_ID_COLORCONTROL_DRIFTCOMPENSATION = "colorcontrol-driftcompensation"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_DRIFTCOMPENSATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_DRIFTCOMPENSATION); + + public static final String CHANNEL_NAME_COLORCONTROL_COMPENSATIONTEXT = "CompensationText"; + public static final String CHANNEL_LABEL_COLORCONTROL_COMPENSATIONTEXT = "Compensation Text"; + public static final String CHANNEL_ID_COLORCONTROL_COMPENSATIONTEXT = "colorcontrol-compensationtext"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COMPENSATIONTEXT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COMPENSATIONTEXT); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORTEMPERATUREMIREDS = "ColorTemperatureMireds"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORTEMPERATUREMIREDS = "Color Temperature Mireds"; + public static final String CHANNEL_ID_COLORCONTROL_COLORTEMPERATUREMIREDS = "colorcontrol-colortemperaturemireds"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORTEMPERATUREMIREDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORTEMPERATUREMIREDS); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORMODE = "ColorMode"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORMODE = "Color Mode"; + public static final String CHANNEL_ID_COLORCONTROL_COLORMODE = "colorcontrol-colormode"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORMODE); + + public static final String CHANNEL_NAME_COLORCONTROL_OPTIONS = "Options"; + public static final String CHANNEL_LABEL_COLORCONTROL_OPTIONS = "Options"; + public static final String CHANNEL_ID_COLORCONTROL_OPTIONS = "colorcontrol-options"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_OPTIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_OPTIONS); + + public static final String CHANNEL_NAME_COLORCONTROL_NUMBEROFPRIMARIES = "NumberOfPrimaries"; + public static final String CHANNEL_LABEL_COLORCONTROL_NUMBEROFPRIMARIES = "Number Of Primaries"; + public static final String CHANNEL_ID_COLORCONTROL_NUMBEROFPRIMARIES = "colorcontrol-numberofprimaries"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_NUMBEROFPRIMARIES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_NUMBEROFPRIMARIES); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY1X = "Primary1X"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY1X = "Primary1x"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY1X = "colorcontrol-primary1x"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY1X = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY1X); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY1Y = "Primary1Y"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY1Y = "Primary1y"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY1Y = "colorcontrol-primary1y"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY1Y = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY1Y); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY1INTENSITY = "Primary1Intensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY1INTENSITY = "Primary1intensity"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY1INTENSITY = "colorcontrol-primary1intensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY1INTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY1INTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY2X = "Primary2X"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY2X = "Primary2x"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY2X = "colorcontrol-primary2x"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY2X = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY2X); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY2Y = "Primary2Y"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY2Y = "Primary2y"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY2Y = "colorcontrol-primary2y"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY2Y = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY2Y); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY2INTENSITY = "Primary2Intensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY2INTENSITY = "Primary2intensity"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY2INTENSITY = "colorcontrol-primary2intensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY2INTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY2INTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY3X = "Primary3X"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY3X = "Primary3x"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY3X = "colorcontrol-primary3x"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY3X = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY3X); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY3Y = "Primary3Y"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY3Y = "Primary3y"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY3Y = "colorcontrol-primary3y"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY3Y = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY3Y); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY3INTENSITY = "Primary3Intensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY3INTENSITY = "Primary3intensity"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY3INTENSITY = "colorcontrol-primary3intensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY3INTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY3INTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY4X = "Primary4X"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY4X = "Primary4x"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY4X = "colorcontrol-primary4x"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY4X = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY4X); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY4Y = "Primary4Y"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY4Y = "Primary4y"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY4Y = "colorcontrol-primary4y"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY4Y = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY4Y); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY4INTENSITY = "Primary4Intensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY4INTENSITY = "Primary4intensity"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY4INTENSITY = "colorcontrol-primary4intensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY4INTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY4INTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY5X = "Primary5X"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY5X = "Primary5x"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY5X = "colorcontrol-primary5x"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY5X = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY5X); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY5Y = "Primary5Y"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY5Y = "Primary5y"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY5Y = "colorcontrol-primary5y"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY5Y = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY5Y); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY5INTENSITY = "Primary5Intensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY5INTENSITY = "Primary5intensity"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY5INTENSITY = "colorcontrol-primary5intensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY5INTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY5INTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY6X = "Primary6X"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY6X = "Primary6x"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY6X = "colorcontrol-primary6x"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY6X = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY6X); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY6Y = "Primary6Y"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY6Y = "Primary6y"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY6Y = "colorcontrol-primary6y"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY6Y = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY6Y); + + public static final String CHANNEL_NAME_COLORCONTROL_PRIMARY6INTENSITY = "Primary6Intensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_PRIMARY6INTENSITY = "Primary6intensity"; + public static final String CHANNEL_ID_COLORCONTROL_PRIMARY6INTENSITY = "colorcontrol-primary6intensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_PRIMARY6INTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_PRIMARY6INTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_WHITEPOINTX = "WhitePointX"; + public static final String CHANNEL_LABEL_COLORCONTROL_WHITEPOINTX = "White Point X"; + public static final String CHANNEL_ID_COLORCONTROL_WHITEPOINTX = "colorcontrol-whitepointx"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_WHITEPOINTX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_WHITEPOINTX); + + public static final String CHANNEL_NAME_COLORCONTROL_WHITEPOINTY = "WhitePointY"; + public static final String CHANNEL_LABEL_COLORCONTROL_WHITEPOINTY = "White Point Y"; + public static final String CHANNEL_ID_COLORCONTROL_WHITEPOINTY = "colorcontrol-whitepointy"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_WHITEPOINTY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_WHITEPOINTY); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTRX = "ColorPointRx"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTRX = "Color Point Rx"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTRX = "colorcontrol-colorpointrx"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTRX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTRX); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTRY = "ColorPointRy"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTRY = "Color Point Ry"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTRY = "colorcontrol-colorpointry"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTRY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTRY); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTRINTENSITY = "ColorPointRIntensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTRINTENSITY = "Color Point Rintensity"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTRINTENSITY = "colorcontrol-colorpointrintensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTRINTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTRINTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTGX = "ColorPointGx"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTGX = "Color Point Gx"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTGX = "colorcontrol-colorpointgx"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTGX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTGX); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTGY = "ColorPointGy"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTGY = "Color Point Gy"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTGY = "colorcontrol-colorpointgy"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTGY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTGY); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTGINTENSITY = "ColorPointGIntensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTGINTENSITY = "Color Point Gintensity"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTGINTENSITY = "colorcontrol-colorpointgintensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTGINTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTGINTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTBX = "ColorPointBx"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTBX = "Color Point Bx"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTBX = "colorcontrol-colorpointbx"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTBX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTBX); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTBY = "ColorPointBy"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTBY = "Color Point By"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTBY = "colorcontrol-colorpointby"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTBY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTBY); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORPOINTBINTENSITY = "ColorPointBIntensity"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORPOINTBINTENSITY = "Color Point Bintensity"; + public static final String CHANNEL_ID_COLORCONTROL_COLORPOINTBINTENSITY = "colorcontrol-colorpointbintensity"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORPOINTBINTENSITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORPOINTBINTENSITY); + + public static final String CHANNEL_NAME_COLORCONTROL_ENHANCEDCURRENTHUE = "EnhancedCurrentHue"; + public static final String CHANNEL_LABEL_COLORCONTROL_ENHANCEDCURRENTHUE = "Enhanced Current Hue"; + public static final String CHANNEL_ID_COLORCONTROL_ENHANCEDCURRENTHUE = "colorcontrol-enhancedcurrenthue"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_ENHANCEDCURRENTHUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_ENHANCEDCURRENTHUE); + + public static final String CHANNEL_NAME_COLORCONTROL_ENHANCEDCOLORMODE = "EnhancedColorMode"; + public static final String CHANNEL_LABEL_COLORCONTROL_ENHANCEDCOLORMODE = "Enhanced Color Mode"; + public static final String CHANNEL_ID_COLORCONTROL_ENHANCEDCOLORMODE = "colorcontrol-enhancedcolormode"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_ENHANCEDCOLORMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_ENHANCEDCOLORMODE); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORLOOPACTIVE = "ColorLoopActive"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORLOOPACTIVE = "Color Loop Active"; + public static final String CHANNEL_ID_COLORCONTROL_COLORLOOPACTIVE = "colorcontrol-colorloopactive"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORLOOPACTIVE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORLOOPACTIVE); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORLOOPDIRECTION = "ColorLoopDirection"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORLOOPDIRECTION = "Color Loop Direction"; + public static final String CHANNEL_ID_COLORCONTROL_COLORLOOPDIRECTION = "colorcontrol-colorloopdirection"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORLOOPDIRECTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORLOOPDIRECTION); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORLOOPTIME = "ColorLoopTime"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORLOOPTIME = "Color Loop Time"; + public static final String CHANNEL_ID_COLORCONTROL_COLORLOOPTIME = "colorcontrol-colorlooptime"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORLOOPTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORLOOPTIME); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORLOOPSTARTENHANCEDHUE = "ColorLoopStartEnhancedHue"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORLOOPSTARTENHANCEDHUE = "Color Loop Start Enhanced Hue"; + public static final String CHANNEL_ID_COLORCONTROL_COLORLOOPSTARTENHANCEDHUE = "colorcontrol-colorloopstartenhancedhue"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORLOOPSTARTENHANCEDHUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORLOOPSTARTENHANCEDHUE); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORLOOPSTOREDENHANCEDHUE = "ColorLoopStoredEnhancedHue"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORLOOPSTOREDENHANCEDHUE = "Color Loop Stored Enhanced Hue"; + public static final String CHANNEL_ID_COLORCONTROL_COLORLOOPSTOREDENHANCEDHUE = "colorcontrol-colorloopstoredenhancedhue"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORLOOPSTOREDENHANCEDHUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORLOOPSTOREDENHANCEDHUE); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORCAPABILITIES = "ColorCapabilities"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORCAPABILITIES = "Color Capabilities"; + public static final String CHANNEL_ID_COLORCONTROL_COLORCAPABILITIES = "colorcontrol-colorcapabilities"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORCAPABILITIES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORCAPABILITIES); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORTEMPPHYSICALMINMIREDS = "ColorTempPhysicalMinMireds"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORTEMPPHYSICALMINMIREDS = "Color Temp Physical Min Mireds"; + public static final String CHANNEL_ID_COLORCONTROL_COLORTEMPPHYSICALMINMIREDS = "colorcontrol-colortempphysicalminmireds"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORTEMPPHYSICALMINMIREDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORTEMPPHYSICALMINMIREDS); + + public static final String CHANNEL_NAME_COLORCONTROL_COLORTEMPPHYSICALMAXMIREDS = "ColorTempPhysicalMaxMireds"; + public static final String CHANNEL_LABEL_COLORCONTROL_COLORTEMPPHYSICALMAXMIREDS = "Color Temp Physical Max Mireds"; + public static final String CHANNEL_ID_COLORCONTROL_COLORTEMPPHYSICALMAXMIREDS = "colorcontrol-colortempphysicalmaxmireds"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COLORTEMPPHYSICALMAXMIREDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COLORTEMPPHYSICALMAXMIREDS); + + public static final String CHANNEL_NAME_COLORCONTROL_COUPLECOLORTEMPTOLEVELMINMIREDS = "CoupleColorTempToLevelMinMireds"; + public static final String CHANNEL_LABEL_COLORCONTROL_COUPLECOLORTEMPTOLEVELMINMIREDS = "Couple Color Temp To Level Min Mireds"; + public static final String CHANNEL_ID_COLORCONTROL_COUPLECOLORTEMPTOLEVELMINMIREDS = "colorcontrol-couplecolortemptolevelminmireds"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_COUPLECOLORTEMPTOLEVELMINMIREDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_COUPLECOLORTEMPTOLEVELMINMIREDS); + + public static final String CHANNEL_NAME_COLORCONTROL_STARTUPCOLORTEMPERATUREMIREDS = "StartUpColorTemperatureMireds"; + public static final String CHANNEL_LABEL_COLORCONTROL_STARTUPCOLORTEMPERATUREMIREDS = "Start Up Color Temperature Mireds"; + public static final String CHANNEL_ID_COLORCONTROL_STARTUPCOLORTEMPERATUREMIREDS = "colorcontrol-startupcolortemperaturemireds"; + public static final ChannelTypeUID CHANNEL_COLORCONTROL_STARTUPCOLORTEMPERATUREMIREDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COLORCONTROL_STARTUPCOLORTEMPERATUREMIREDS); + + // CommissionerControl Cluster + public static final String CHANNEL_NAME_COMMISSIONERCONTROL_SUPPORTEDDEVICECATEGORIES = "SupportedDeviceCategories"; + public static final String CHANNEL_LABEL_COMMISSIONERCONTROL_SUPPORTEDDEVICECATEGORIES = "Supported Device Categories"; + public static final String CHANNEL_ID_COMMISSIONERCONTROL_SUPPORTEDDEVICECATEGORIES = "commissionercontrol-supporteddevicecategories"; + public static final ChannelTypeUID CHANNEL_COMMISSIONERCONTROL_SUPPORTEDDEVICECATEGORIES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_COMMISSIONERCONTROL_SUPPORTEDDEVICECATEGORIES); + + // ConcentrationMeasurement Cluster + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_MEASUREDVALUE = "MeasuredValue"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_MEASUREDVALUE = "Measured Value"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_MEASUREDVALUE = "concentrationmeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_MEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_MEASUREDVALUE); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_MINMEASUREDVALUE = "MinMeasuredValue"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_MINMEASUREDVALUE = "Min Measured Value"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_MINMEASUREDVALUE = "concentrationmeasurement-minmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_MINMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_MINMEASUREDVALUE); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_MAXMEASUREDVALUE = "MaxMeasuredValue"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_MAXMEASUREDVALUE = "Max Measured Value"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_MAXMEASUREDVALUE = "concentrationmeasurement-maxmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_MAXMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_MAXMEASUREDVALUE); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUE = "PeakMeasuredValue"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUE = "Peak Measured Value"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUE = "concentrationmeasurement-peakmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUE); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUEWINDOW = "PeakMeasuredValueWindow"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUEWINDOW = "Peak Measured Value Window"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUEWINDOW = "concentrationmeasurement-peakmeasuredvaluewindow"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUEWINDOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_PEAKMEASUREDVALUEWINDOW); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUE = "AverageMeasuredValue"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUE = "Average Measured Value"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUE = "concentrationmeasurement-averagemeasuredvalue"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUE); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUEWINDOW = "AverageMeasuredValueWindow"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUEWINDOW = "Average Measured Value Window"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUEWINDOW = "concentrationmeasurement-averagemeasuredvaluewindow"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUEWINDOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_AVERAGEMEASUREDVALUEWINDOW); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_UNCERTAINTY = "Uncertainty"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_UNCERTAINTY = "Uncertainty"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_UNCERTAINTY = "concentrationmeasurement-uncertainty"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_UNCERTAINTY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_UNCERTAINTY); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_MEASUREMENTUNIT = "MeasurementUnit"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_MEASUREMENTUNIT = "Measurement Unit"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_MEASUREMENTUNIT = "concentrationmeasurement-measurementunit"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_MEASUREMENTUNIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_MEASUREMENTUNIT); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_MEASUREMENTMEDIUM = "MeasurementMedium"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_MEASUREMENTMEDIUM = "Measurement Medium"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_MEASUREMENTMEDIUM = "concentrationmeasurement-measurementmedium"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_MEASUREMENTMEDIUM = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_MEASUREMENTMEDIUM); + + public static final String CHANNEL_NAME_CONCENTRATIONMEASUREMENT_LEVELVALUE = "LevelValue"; + public static final String CHANNEL_LABEL_CONCENTRATIONMEASUREMENT_LEVELVALUE = "Level Value"; + public static final String CHANNEL_ID_CONCENTRATIONMEASUREMENT_LEVELVALUE = "concentrationmeasurement-levelvalue"; + public static final ChannelTypeUID CHANNEL_CONCENTRATIONMEASUREMENT_LEVELVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONCENTRATIONMEASUREMENT_LEVELVALUE); + + // ContentAppObserver Cluster + // ContentControl Cluster + public static final String CHANNEL_NAME_CONTENTCONTROL_ENABLED = "Enabled"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_ENABLED = "Enabled"; + public static final String CHANNEL_ID_CONTENTCONTROL_ENABLED = "contentcontrol-enabled"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_ENABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_ENABLED); + + public static final String CHANNEL_NAME_CONTENTCONTROL_ONDEMANDRATINGS = "OnDemandRatings"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_ONDEMANDRATINGS = "On Demand Ratings"; + public static final String CHANNEL_ID_CONTENTCONTROL_ONDEMANDRATINGS = "contentcontrol-ondemandratings"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_ONDEMANDRATINGS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_ONDEMANDRATINGS); + + public static final String CHANNEL_NAME_CONTENTCONTROL_ONDEMANDRATINGTHRESHOLD = "OnDemandRatingThreshold"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_ONDEMANDRATINGTHRESHOLD = "On Demand Rating Threshold"; + public static final String CHANNEL_ID_CONTENTCONTROL_ONDEMANDRATINGTHRESHOLD = "contentcontrol-ondemandratingthreshold"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_ONDEMANDRATINGTHRESHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_ONDEMANDRATINGTHRESHOLD); + + public static final String CHANNEL_NAME_CONTENTCONTROL_SCHEDULEDCONTENTRATINGS = "ScheduledContentRatings"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_SCHEDULEDCONTENTRATINGS = "Scheduled Content Ratings"; + public static final String CHANNEL_ID_CONTENTCONTROL_SCHEDULEDCONTENTRATINGS = "contentcontrol-scheduledcontentratings"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_SCHEDULEDCONTENTRATINGS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_SCHEDULEDCONTENTRATINGS); + + public static final String CHANNEL_NAME_CONTENTCONTROL_SCHEDULEDCONTENTRATINGTHRESHOLD = "ScheduledContentRatingThreshold"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_SCHEDULEDCONTENTRATINGTHRESHOLD = "Scheduled Content Rating Threshold"; + public static final String CHANNEL_ID_CONTENTCONTROL_SCHEDULEDCONTENTRATINGTHRESHOLD = "contentcontrol-scheduledcontentratingthreshold"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_SCHEDULEDCONTENTRATINGTHRESHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_SCHEDULEDCONTENTRATINGTHRESHOLD); + + public static final String CHANNEL_NAME_CONTENTCONTROL_SCREENDAILYTIME = "ScreenDailyTime"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_SCREENDAILYTIME = "Screen Daily Time"; + public static final String CHANNEL_ID_CONTENTCONTROL_SCREENDAILYTIME = "contentcontrol-screendailytime"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_SCREENDAILYTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_SCREENDAILYTIME); + + public static final String CHANNEL_NAME_CONTENTCONTROL_REMAININGSCREENTIME = "RemainingScreenTime"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_REMAININGSCREENTIME = "Remaining Screen Time"; + public static final String CHANNEL_ID_CONTENTCONTROL_REMAININGSCREENTIME = "contentcontrol-remainingscreentime"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_REMAININGSCREENTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_REMAININGSCREENTIME); + + public static final String CHANNEL_NAME_CONTENTCONTROL_BLOCKUNRATED = "BlockUnrated"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_BLOCKUNRATED = "Block Unrated"; + public static final String CHANNEL_ID_CONTENTCONTROL_BLOCKUNRATED = "contentcontrol-blockunrated"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_BLOCKUNRATED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_BLOCKUNRATED); + + public static final String CHANNEL_NAME_CONTENTCONTROL_BLOCKCHANNELLIST = "BlockChannelList"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_BLOCKCHANNELLIST = "Block Channel List"; + public static final String CHANNEL_ID_CONTENTCONTROL_BLOCKCHANNELLIST = "contentcontrol-blockchannellist"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_BLOCKCHANNELLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_BLOCKCHANNELLIST); + + public static final String CHANNEL_NAME_CONTENTCONTROL_BLOCKAPPLICATIONLIST = "BlockApplicationList"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_BLOCKAPPLICATIONLIST = "Block Application List"; + public static final String CHANNEL_ID_CONTENTCONTROL_BLOCKAPPLICATIONLIST = "contentcontrol-blockapplicationlist"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_BLOCKAPPLICATIONLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_BLOCKAPPLICATIONLIST); + + public static final String CHANNEL_NAME_CONTENTCONTROL_BLOCKCONTENTTIMEWINDOW = "BlockContentTimeWindow"; + public static final String CHANNEL_LABEL_CONTENTCONTROL_BLOCKCONTENTTIMEWINDOW = "Block Content Time Window"; + public static final String CHANNEL_ID_CONTENTCONTROL_BLOCKCONTENTTIMEWINDOW = "contentcontrol-blockcontenttimewindow"; + public static final ChannelTypeUID CHANNEL_CONTENTCONTROL_BLOCKCONTENTTIMEWINDOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTCONTROL_BLOCKCONTENTTIMEWINDOW); + + // ContentLauncher Cluster + public static final String CHANNEL_NAME_CONTENTLAUNCHER_ACCEPTHEADER = "AcceptHeader"; + public static final String CHANNEL_LABEL_CONTENTLAUNCHER_ACCEPTHEADER = "Accept Header"; + public static final String CHANNEL_ID_CONTENTLAUNCHER_ACCEPTHEADER = "contentlauncher-acceptheader"; + public static final ChannelTypeUID CHANNEL_CONTENTLAUNCHER_ACCEPTHEADER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTLAUNCHER_ACCEPTHEADER); + + public static final String CHANNEL_NAME_CONTENTLAUNCHER_SUPPORTEDSTREAMINGPROTOCOLS = "SupportedStreamingProtocols"; + public static final String CHANNEL_LABEL_CONTENTLAUNCHER_SUPPORTEDSTREAMINGPROTOCOLS = "Supported Streaming Protocols"; + public static final String CHANNEL_ID_CONTENTLAUNCHER_SUPPORTEDSTREAMINGPROTOCOLS = "contentlauncher-supportedstreamingprotocols"; + public static final ChannelTypeUID CHANNEL_CONTENTLAUNCHER_SUPPORTEDSTREAMINGPROTOCOLS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_CONTENTLAUNCHER_SUPPORTEDSTREAMINGPROTOCOLS); + + // Descriptor Cluster + public static final String CHANNEL_NAME_DESCRIPTOR_DEVICETYPELIST = "DeviceTypeList"; + public static final String CHANNEL_LABEL_DESCRIPTOR_DEVICETYPELIST = "Device Type List"; + public static final String CHANNEL_ID_DESCRIPTOR_DEVICETYPELIST = "descriptor-devicetypelist"; + public static final ChannelTypeUID CHANNEL_DESCRIPTOR_DEVICETYPELIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DESCRIPTOR_DEVICETYPELIST); + + public static final String CHANNEL_NAME_DESCRIPTOR_SERVERLIST = "ServerList"; + public static final String CHANNEL_LABEL_DESCRIPTOR_SERVERLIST = "Server List"; + public static final String CHANNEL_ID_DESCRIPTOR_SERVERLIST = "descriptor-serverlist"; + public static final ChannelTypeUID CHANNEL_DESCRIPTOR_SERVERLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DESCRIPTOR_SERVERLIST); + + public static final String CHANNEL_NAME_DESCRIPTOR_CLIENTLIST = "ClientList"; + public static final String CHANNEL_LABEL_DESCRIPTOR_CLIENTLIST = "Client List"; + public static final String CHANNEL_ID_DESCRIPTOR_CLIENTLIST = "descriptor-clientlist"; + public static final ChannelTypeUID CHANNEL_DESCRIPTOR_CLIENTLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DESCRIPTOR_CLIENTLIST); + + public static final String CHANNEL_NAME_DESCRIPTOR_PARTSLIST = "PartsList"; + public static final String CHANNEL_LABEL_DESCRIPTOR_PARTSLIST = "Parts List"; + public static final String CHANNEL_ID_DESCRIPTOR_PARTSLIST = "descriptor-partslist"; + public static final ChannelTypeUID CHANNEL_DESCRIPTOR_PARTSLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DESCRIPTOR_PARTSLIST); + + public static final String CHANNEL_NAME_DESCRIPTOR_TAGLIST = "TagList"; + public static final String CHANNEL_LABEL_DESCRIPTOR_TAGLIST = "Tag List"; + public static final String CHANNEL_ID_DESCRIPTOR_TAGLIST = "descriptor-taglist"; + public static final ChannelTypeUID CHANNEL_DESCRIPTOR_TAGLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DESCRIPTOR_TAGLIST); + + // DeviceEnergyManagement Cluster + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_ESATYPE = "EsaType"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_ESATYPE = "Esa Type"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_ESATYPE = "deviceenergymanagement-esatype"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_ESATYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_ESATYPE); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_ESACANGENERATE = "EsaCanGenerate"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_ESACANGENERATE = "Esa Can Generate"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_ESACANGENERATE = "deviceenergymanagement-esacangenerate"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_ESACANGENERATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_ESACANGENERATE); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_ESASTATE = "EsaState"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_ESASTATE = "Esa State"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_ESASTATE = "deviceenergymanagement-esastate"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_ESASTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_ESASTATE); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_ABSMINPOWER = "AbsMinPower"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_ABSMINPOWER = "Abs Min Power"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_ABSMINPOWER = "deviceenergymanagement-absminpower"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_ABSMINPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_ABSMINPOWER); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_ABSMAXPOWER = "AbsMaxPower"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_ABSMAXPOWER = "Abs Max Power"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_ABSMAXPOWER = "deviceenergymanagement-absmaxpower"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_ABSMAXPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_ABSMAXPOWER); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_POWERADJUSTMENTCAPABILITY = "PowerAdjustmentCapability"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_POWERADJUSTMENTCAPABILITY = "Power Adjustment Capability"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_POWERADJUSTMENTCAPABILITY = "deviceenergymanagement-poweradjustmentcapability"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_POWERADJUSTMENTCAPABILITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_POWERADJUSTMENTCAPABILITY); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_FORECAST = "Forecast"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_FORECAST = "Forecast"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_FORECAST = "deviceenergymanagement-forecast"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_FORECAST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_FORECAST); + + public static final String CHANNEL_NAME_DEVICEENERGYMANAGEMENT_OPTOUTSTATE = "OptOutState"; + public static final String CHANNEL_LABEL_DEVICEENERGYMANAGEMENT_OPTOUTSTATE = "Opt Out State"; + public static final String CHANNEL_ID_DEVICEENERGYMANAGEMENT_OPTOUTSTATE = "deviceenergymanagement-optoutstate"; + public static final ChannelTypeUID CHANNEL_DEVICEENERGYMANAGEMENT_OPTOUTSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DEVICEENERGYMANAGEMENT_OPTOUTSTATE); + + // DeviceEnergyManagementMode Cluster + // DiagnosticLogs Cluster + // DishwasherAlarm Cluster + // DishwasherMode Cluster + // DoorLock Cluster + public static final String CHANNEL_NAME_DOORLOCK_LOCKSTATE = "LockState"; + public static final String CHANNEL_LABEL_DOORLOCK_LOCKSTATE = "Lock State"; + public static final String CHANNEL_ID_DOORLOCK_LOCKSTATE = "doorlock-lockstate"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_LOCKSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_LOCKSTATE); + + public static final String CHANNEL_NAME_DOORLOCK_LOCKTYPE = "LockType"; + public static final String CHANNEL_LABEL_DOORLOCK_LOCKTYPE = "Lock Type"; + public static final String CHANNEL_ID_DOORLOCK_LOCKTYPE = "doorlock-locktype"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_LOCKTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_LOCKTYPE); + + public static final String CHANNEL_NAME_DOORLOCK_ACTUATORENABLED = "ActuatorEnabled"; + public static final String CHANNEL_LABEL_DOORLOCK_ACTUATORENABLED = "Actuator Enabled"; + public static final String CHANNEL_ID_DOORLOCK_ACTUATORENABLED = "doorlock-actuatorenabled"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ACTUATORENABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ACTUATORENABLED); + + public static final String CHANNEL_NAME_DOORLOCK_DOORSTATE = "DoorState"; + public static final String CHANNEL_LABEL_DOORLOCK_DOORSTATE = "Door State"; + public static final String CHANNEL_ID_DOORLOCK_DOORSTATE = "doorlock-doorstate"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_DOORSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_DOORSTATE); + + public static final String CHANNEL_NAME_DOORLOCK_DOOROPENEVENTS = "DoorOpenEvents"; + public static final String CHANNEL_LABEL_DOORLOCK_DOOROPENEVENTS = "Door Open Events"; + public static final String CHANNEL_ID_DOORLOCK_DOOROPENEVENTS = "doorlock-dooropenevents"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_DOOROPENEVENTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_DOOROPENEVENTS); + + public static final String CHANNEL_NAME_DOORLOCK_DOORCLOSEDEVENTS = "DoorClosedEvents"; + public static final String CHANNEL_LABEL_DOORLOCK_DOORCLOSEDEVENTS = "Door Closed Events"; + public static final String CHANNEL_ID_DOORLOCK_DOORCLOSEDEVENTS = "doorlock-doorclosedevents"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_DOORCLOSEDEVENTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_DOORCLOSEDEVENTS); + + public static final String CHANNEL_NAME_DOORLOCK_OPENPERIOD = "OpenPeriod"; + public static final String CHANNEL_LABEL_DOORLOCK_OPENPERIOD = "Open Period"; + public static final String CHANNEL_ID_DOORLOCK_OPENPERIOD = "doorlock-openperiod"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_OPENPERIOD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_OPENPERIOD); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFTOTALUSERSSUPPORTED = "NumberOfTotalUsersSupported"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFTOTALUSERSSUPPORTED = "Number Of Total Users Supported"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFTOTALUSERSSUPPORTED = "doorlock-numberoftotaluserssupported"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFTOTALUSERSSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFTOTALUSERSSUPPORTED); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFPINUSERSSUPPORTED = "NumberOfPinUsersSupported"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFPINUSERSSUPPORTED = "Number Of Pin Users Supported"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFPINUSERSSUPPORTED = "doorlock-numberofpinuserssupported"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFPINUSERSSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFPINUSERSSUPPORTED); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFRFIDUSERSSUPPORTED = "NumberOfRfidUsersSupported"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFRFIDUSERSSUPPORTED = "Number Of Rfid Users Supported"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFRFIDUSERSSUPPORTED = "doorlock-numberofrfiduserssupported"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFRFIDUSERSSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFRFIDUSERSSUPPORTED); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFWEEKDAYSCHEDULESSUPPORTEDPERUSER = "NumberOfWeekDaySchedulesSupportedPerUser"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFWEEKDAYSCHEDULESSUPPORTEDPERUSER = "Number Of Week Day Schedules Supported Per User"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFWEEKDAYSCHEDULESSUPPORTEDPERUSER = "doorlock-numberofweekdayschedulessupportedperuser"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFWEEKDAYSCHEDULESSUPPORTEDPERUSER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFWEEKDAYSCHEDULESSUPPORTEDPERUSER); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFYEARDAYSCHEDULESSUPPORTEDPERUSER = "NumberOfYearDaySchedulesSupportedPerUser"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFYEARDAYSCHEDULESSUPPORTEDPERUSER = "Number Of Year Day Schedules Supported Per User"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFYEARDAYSCHEDULESSUPPORTEDPERUSER = "doorlock-numberofyeardayschedulessupportedperuser"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFYEARDAYSCHEDULESSUPPORTEDPERUSER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFYEARDAYSCHEDULESSUPPORTEDPERUSER); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFHOLIDAYSCHEDULESSUPPORTED = "NumberOfHolidaySchedulesSupported"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFHOLIDAYSCHEDULESSUPPORTED = "Number Of Holiday Schedules Supported"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFHOLIDAYSCHEDULESSUPPORTED = "doorlock-numberofholidayschedulessupported"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFHOLIDAYSCHEDULESSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFHOLIDAYSCHEDULESSUPPORTED); + + public static final String CHANNEL_NAME_DOORLOCK_MAXPINCODELENGTH = "MaxPinCodeLength"; + public static final String CHANNEL_LABEL_DOORLOCK_MAXPINCODELENGTH = "Max Pin Code Length"; + public static final String CHANNEL_ID_DOORLOCK_MAXPINCODELENGTH = "doorlock-maxpincodelength"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_MAXPINCODELENGTH = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_MAXPINCODELENGTH); + + public static final String CHANNEL_NAME_DOORLOCK_MINPINCODELENGTH = "MinPinCodeLength"; + public static final String CHANNEL_LABEL_DOORLOCK_MINPINCODELENGTH = "Min Pin Code Length"; + public static final String CHANNEL_ID_DOORLOCK_MINPINCODELENGTH = "doorlock-minpincodelength"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_MINPINCODELENGTH = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_MINPINCODELENGTH); + + public static final String CHANNEL_NAME_DOORLOCK_MAXRFIDCODELENGTH = "MaxRfidCodeLength"; + public static final String CHANNEL_LABEL_DOORLOCK_MAXRFIDCODELENGTH = "Max Rfid Code Length"; + public static final String CHANNEL_ID_DOORLOCK_MAXRFIDCODELENGTH = "doorlock-maxrfidcodelength"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_MAXRFIDCODELENGTH = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_MAXRFIDCODELENGTH); + + public static final String CHANNEL_NAME_DOORLOCK_MINRFIDCODELENGTH = "MinRfidCodeLength"; + public static final String CHANNEL_LABEL_DOORLOCK_MINRFIDCODELENGTH = "Min Rfid Code Length"; + public static final String CHANNEL_ID_DOORLOCK_MINRFIDCODELENGTH = "doorlock-minrfidcodelength"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_MINRFIDCODELENGTH = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_MINRFIDCODELENGTH); + + public static final String CHANNEL_NAME_DOORLOCK_CREDENTIALRULESSUPPORT = "CredentialRulesSupport"; + public static final String CHANNEL_LABEL_DOORLOCK_CREDENTIALRULESSUPPORT = "Credential Rules Support"; + public static final String CHANNEL_ID_DOORLOCK_CREDENTIALRULESSUPPORT = "doorlock-credentialrulessupport"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_CREDENTIALRULESSUPPORT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_CREDENTIALRULESSUPPORT); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFCREDENTIALSSUPPORTEDPERUSER = "NumberOfCredentialsSupportedPerUser"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFCREDENTIALSSUPPORTEDPERUSER = "Number Of Credentials Supported Per User"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFCREDENTIALSSUPPORTEDPERUSER = "doorlock-numberofcredentialssupportedperuser"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFCREDENTIALSSUPPORTEDPERUSER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFCREDENTIALSSUPPORTEDPERUSER); + + public static final String CHANNEL_NAME_DOORLOCK_LANGUAGE = "Language"; + public static final String CHANNEL_LABEL_DOORLOCK_LANGUAGE = "Language"; + public static final String CHANNEL_ID_DOORLOCK_LANGUAGE = "doorlock-language"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_LANGUAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_LANGUAGE); + + public static final String CHANNEL_NAME_DOORLOCK_LEDSETTINGS = "LedSettings"; + public static final String CHANNEL_LABEL_DOORLOCK_LEDSETTINGS = "Led Settings"; + public static final String CHANNEL_ID_DOORLOCK_LEDSETTINGS = "doorlock-ledsettings"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_LEDSETTINGS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_LEDSETTINGS); + + public static final String CHANNEL_NAME_DOORLOCK_AUTORELOCKTIME = "AutoRelockTime"; + public static final String CHANNEL_LABEL_DOORLOCK_AUTORELOCKTIME = "Auto Relock Time"; + public static final String CHANNEL_ID_DOORLOCK_AUTORELOCKTIME = "doorlock-autorelocktime"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_AUTORELOCKTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_AUTORELOCKTIME); + + public static final String CHANNEL_NAME_DOORLOCK_SOUNDVOLUME = "SoundVolume"; + public static final String CHANNEL_LABEL_DOORLOCK_SOUNDVOLUME = "Sound Volume"; + public static final String CHANNEL_ID_DOORLOCK_SOUNDVOLUME = "doorlock-soundvolume"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_SOUNDVOLUME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_SOUNDVOLUME); + + public static final String CHANNEL_NAME_DOORLOCK_OPERATINGMODE = "OperatingMode"; + public static final String CHANNEL_LABEL_DOORLOCK_OPERATINGMODE = "Operating Mode"; + public static final String CHANNEL_ID_DOORLOCK_OPERATINGMODE = "doorlock-operatingmode"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_OPERATINGMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_OPERATINGMODE); + + public static final String CHANNEL_NAME_DOORLOCK_SUPPORTEDOPERATINGMODES = "SupportedOperatingModes"; + public static final String CHANNEL_LABEL_DOORLOCK_SUPPORTEDOPERATINGMODES = "Supported Operating Modes"; + public static final String CHANNEL_ID_DOORLOCK_SUPPORTEDOPERATINGMODES = "doorlock-supportedoperatingmodes"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_SUPPORTEDOPERATINGMODES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_SUPPORTEDOPERATINGMODES); + + public static final String CHANNEL_NAME_DOORLOCK_DEFAULTCONFIGURATIONREGISTER = "DefaultConfigurationRegister"; + public static final String CHANNEL_LABEL_DOORLOCK_DEFAULTCONFIGURATIONREGISTER = "Default Configuration Register"; + public static final String CHANNEL_ID_DOORLOCK_DEFAULTCONFIGURATIONREGISTER = "doorlock-defaultconfigurationregister"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_DEFAULTCONFIGURATIONREGISTER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_DEFAULTCONFIGURATIONREGISTER); + + public static final String CHANNEL_NAME_DOORLOCK_ENABLELOCALPROGRAMMING = "EnableLocalProgramming"; + public static final String CHANNEL_LABEL_DOORLOCK_ENABLELOCALPROGRAMMING = "Enable Local Programming"; + public static final String CHANNEL_ID_DOORLOCK_ENABLELOCALPROGRAMMING = "doorlock-enablelocalprogramming"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ENABLELOCALPROGRAMMING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ENABLELOCALPROGRAMMING); + + public static final String CHANNEL_NAME_DOORLOCK_ENABLEONETOUCHLOCKING = "EnableOneTouchLocking"; + public static final String CHANNEL_LABEL_DOORLOCK_ENABLEONETOUCHLOCKING = "Enable One Touch Locking"; + public static final String CHANNEL_ID_DOORLOCK_ENABLEONETOUCHLOCKING = "doorlock-enableonetouchlocking"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ENABLEONETOUCHLOCKING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ENABLEONETOUCHLOCKING); + + public static final String CHANNEL_NAME_DOORLOCK_ENABLEINSIDESTATUSLED = "EnableInsideStatusLed"; + public static final String CHANNEL_LABEL_DOORLOCK_ENABLEINSIDESTATUSLED = "Enable Inside Status Led"; + public static final String CHANNEL_ID_DOORLOCK_ENABLEINSIDESTATUSLED = "doorlock-enableinsidestatusled"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ENABLEINSIDESTATUSLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ENABLEINSIDESTATUSLED); + + public static final String CHANNEL_NAME_DOORLOCK_ENABLEPRIVACYMODEBUTTON = "EnablePrivacyModeButton"; + public static final String CHANNEL_LABEL_DOORLOCK_ENABLEPRIVACYMODEBUTTON = "Enable Privacy Mode Button"; + public static final String CHANNEL_ID_DOORLOCK_ENABLEPRIVACYMODEBUTTON = "doorlock-enableprivacymodebutton"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ENABLEPRIVACYMODEBUTTON = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ENABLEPRIVACYMODEBUTTON); + + public static final String CHANNEL_NAME_DOORLOCK_LOCALPROGRAMMINGFEATURES = "LocalProgrammingFeatures"; + public static final String CHANNEL_LABEL_DOORLOCK_LOCALPROGRAMMINGFEATURES = "Local Programming Features"; + public static final String CHANNEL_ID_DOORLOCK_LOCALPROGRAMMINGFEATURES = "doorlock-localprogrammingfeatures"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_LOCALPROGRAMMINGFEATURES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_LOCALPROGRAMMINGFEATURES); + + public static final String CHANNEL_NAME_DOORLOCK_WRONGCODEENTRYLIMIT = "WrongCodeEntryLimit"; + public static final String CHANNEL_LABEL_DOORLOCK_WRONGCODEENTRYLIMIT = "Wrong Code Entry Limit"; + public static final String CHANNEL_ID_DOORLOCK_WRONGCODEENTRYLIMIT = "doorlock-wrongcodeentrylimit"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_WRONGCODEENTRYLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_WRONGCODEENTRYLIMIT); + + public static final String CHANNEL_NAME_DOORLOCK_USERCODETEMPORARYDISABLETIME = "UserCodeTemporaryDisableTime"; + public static final String CHANNEL_LABEL_DOORLOCK_USERCODETEMPORARYDISABLETIME = "User Code Temporary Disable Time"; + public static final String CHANNEL_ID_DOORLOCK_USERCODETEMPORARYDISABLETIME = "doorlock-usercodetemporarydisabletime"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_USERCODETEMPORARYDISABLETIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_USERCODETEMPORARYDISABLETIME); + + public static final String CHANNEL_NAME_DOORLOCK_SENDPINOVERTHEAIR = "SendPinOverTheAir"; + public static final String CHANNEL_LABEL_DOORLOCK_SENDPINOVERTHEAIR = "Send Pin Over The Air"; + public static final String CHANNEL_ID_DOORLOCK_SENDPINOVERTHEAIR = "doorlock-sendpinovertheair"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_SENDPINOVERTHEAIR = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_SENDPINOVERTHEAIR); + + public static final String CHANNEL_NAME_DOORLOCK_REQUIREPINFORREMOTEOPERATION = "RequirePinForRemoteOperation"; + public static final String CHANNEL_LABEL_DOORLOCK_REQUIREPINFORREMOTEOPERATION = "Require Pin For Remote Operation"; + public static final String CHANNEL_ID_DOORLOCK_REQUIREPINFORREMOTEOPERATION = "doorlock-requirepinforremoteoperation"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_REQUIREPINFORREMOTEOPERATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_REQUIREPINFORREMOTEOPERATION); + + public static final String CHANNEL_NAME_DOORLOCK_EXPIRINGUSERTIMEOUT = "ExpiringUserTimeout"; + public static final String CHANNEL_LABEL_DOORLOCK_EXPIRINGUSERTIMEOUT = "Expiring User Timeout"; + public static final String CHANNEL_ID_DOORLOCK_EXPIRINGUSERTIMEOUT = "doorlock-expiringusertimeout"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_EXPIRINGUSERTIMEOUT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_EXPIRINGUSERTIMEOUT); + + public static final String CHANNEL_NAME_DOORLOCK_ALARMMASK = "AlarmMask"; + public static final String CHANNEL_LABEL_DOORLOCK_ALARMMASK = "Alarm Mask"; + public static final String CHANNEL_ID_DOORLOCK_ALARMMASK = "doorlock-alarmmask"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALARMMASK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALARMMASK); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROREADERVERIFICATIONKEY = "AliroReaderVerificationKey"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROREADERVERIFICATIONKEY = "Aliro Reader Verification Key"; + public static final String CHANNEL_ID_DOORLOCK_ALIROREADERVERIFICATIONKEY = "doorlock-aliroreaderverificationkey"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROREADERVERIFICATIONKEY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROREADERVERIFICATIONKEY); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROREADERGROUPIDENTIFIER = "AliroReaderGroupIdentifier"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROREADERGROUPIDENTIFIER = "Aliro Reader Group Identifier"; + public static final String CHANNEL_ID_DOORLOCK_ALIROREADERGROUPIDENTIFIER = "doorlock-aliroreadergroupidentifier"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROREADERGROUPIDENTIFIER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROREADERGROUPIDENTIFIER); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROREADERGROUPSUBIDENTIFIER = "AliroReaderGroupSubIdentifier"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROREADERGROUPSUBIDENTIFIER = "Aliro Reader Group Sub Identifier"; + public static final String CHANNEL_ID_DOORLOCK_ALIROREADERGROUPSUBIDENTIFIER = "doorlock-aliroreadergroupsubidentifier"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROREADERGROUPSUBIDENTIFIER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROREADERGROUPSUBIDENTIFIER); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROEXPEDITEDTRANSACTIONSUPPORTEDPROTOCOLVERSIONS = "AliroExpeditedTransactionSupportedProtocolVersions"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROEXPEDITEDTRANSACTIONSUPPORTEDPROTOCOLVERSIONS = "Aliro Expedited Transaction Supported Protocol Versions"; + public static final String CHANNEL_ID_DOORLOCK_ALIROEXPEDITEDTRANSACTIONSUPPORTEDPROTOCOLVERSIONS = "doorlock-aliroexpeditedtransactionsupportedprotocolversions"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROEXPEDITEDTRANSACTIONSUPPORTEDPROTOCOLVERSIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROEXPEDITEDTRANSACTIONSUPPORTEDPROTOCOLVERSIONS); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROGROUPRESOLVINGKEY = "AliroGroupResolvingKey"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROGROUPRESOLVINGKEY = "Aliro Group Resolving Key"; + public static final String CHANNEL_ID_DOORLOCK_ALIROGROUPRESOLVINGKEY = "doorlock-alirogroupresolvingkey"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROGROUPRESOLVINGKEY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROGROUPRESOLVINGKEY); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROSUPPORTEDBLEUWBPROTOCOLVERSIONS = "AliroSupportedBleuwbProtocolVersions"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROSUPPORTEDBLEUWBPROTOCOLVERSIONS = "Aliro Supported Bleuwb Protocol Versions"; + public static final String CHANNEL_ID_DOORLOCK_ALIROSUPPORTEDBLEUWBPROTOCOLVERSIONS = "doorlock-alirosupportedbleuwbprotocolversions"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROSUPPORTEDBLEUWBPROTOCOLVERSIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROSUPPORTEDBLEUWBPROTOCOLVERSIONS); + + public static final String CHANNEL_NAME_DOORLOCK_ALIROBLEADVERTISINGVERSION = "AliroBleAdvertisingVersion"; + public static final String CHANNEL_LABEL_DOORLOCK_ALIROBLEADVERTISINGVERSION = "Aliro Ble Advertising Version"; + public static final String CHANNEL_ID_DOORLOCK_ALIROBLEADVERTISINGVERSION = "doorlock-alirobleadvertisingversion"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_ALIROBLEADVERTISINGVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_ALIROBLEADVERTISINGVERSION); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFALIROCREDENTIALISSUERKEYSSUPPORTED = "NumberOfAliroCredentialIssuerKeysSupported"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFALIROCREDENTIALISSUERKEYSSUPPORTED = "Number Of Aliro Credential Issuer Keys Supported"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFALIROCREDENTIALISSUERKEYSSUPPORTED = "doorlock-numberofalirocredentialissuerkeyssupported"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFALIROCREDENTIALISSUERKEYSSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFALIROCREDENTIALISSUERKEYSSUPPORTED); + + public static final String CHANNEL_NAME_DOORLOCK_NUMBEROFALIROENDPOINTKEYSSUPPORTED = "NumberOfAliroEndpointKeysSupported"; + public static final String CHANNEL_LABEL_DOORLOCK_NUMBEROFALIROENDPOINTKEYSSUPPORTED = "Number Of Aliro Endpoint Keys Supported"; + public static final String CHANNEL_ID_DOORLOCK_NUMBEROFALIROENDPOINTKEYSSUPPORTED = "doorlock-numberofaliroendpointkeyssupported"; + public static final ChannelTypeUID CHANNEL_DOORLOCK_NUMBEROFALIROENDPOINTKEYSSUPPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_DOORLOCK_NUMBEROFALIROENDPOINTKEYSSUPPORTED); + + // EcosystemInformation Cluster + public static final String CHANNEL_NAME_ECOSYSTEMINFORMATION_DEVICEDIRECTORY = "DeviceDirectory"; + public static final String CHANNEL_LABEL_ECOSYSTEMINFORMATION_DEVICEDIRECTORY = "Device Directory"; + public static final String CHANNEL_ID_ECOSYSTEMINFORMATION_DEVICEDIRECTORY = "ecosysteminformation-devicedirectory"; + public static final ChannelTypeUID CHANNEL_ECOSYSTEMINFORMATION_DEVICEDIRECTORY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ECOSYSTEMINFORMATION_DEVICEDIRECTORY); + + public static final String CHANNEL_NAME_ECOSYSTEMINFORMATION_LOCATIONDIRECTORY = "LocationDirectory"; + public static final String CHANNEL_LABEL_ECOSYSTEMINFORMATION_LOCATIONDIRECTORY = "Location Directory"; + public static final String CHANNEL_ID_ECOSYSTEMINFORMATION_LOCATIONDIRECTORY = "ecosysteminformation-locationdirectory"; + public static final ChannelTypeUID CHANNEL_ECOSYSTEMINFORMATION_LOCATIONDIRECTORY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ECOSYSTEMINFORMATION_LOCATIONDIRECTORY); + + // ElectricalEnergyMeasurement Cluster + public static final String CHANNEL_NAME_ELECTRICALENERGYMEASUREMENT_ACCURACY = "Accuracy"; + public static final String CHANNEL_LABEL_ELECTRICALENERGYMEASUREMENT_ACCURACY = "Accuracy"; + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_ACCURACY = "electricalenergymeasurement-accuracy"; + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_ACCURACY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_ACCURACY); + + public static final String CHANNEL_NAME_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED = "CumulativeEnergyImported"; + public static final String CHANNEL_LABEL_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED = "Cumulative Energy Imported"; + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED = "electricalenergymeasurement-cumulativeenergyimported"; + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED); + + public static final String CHANNEL_NAME_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED = "CumulativeEnergyExported"; + public static final String CHANNEL_LABEL_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED = "Cumulative Energy Exported"; + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED = "electricalenergymeasurement-cumulativeenergyexported"; + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED); + + public static final String CHANNEL_NAME_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED = "PeriodicEnergyImported"; + public static final String CHANNEL_LABEL_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED = "Periodic Energy Imported"; + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED = "electricalenergymeasurement-periodicenergyimported"; + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED); + + public static final String CHANNEL_NAME_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED = "PeriodicEnergyExported"; + public static final String CHANNEL_LABEL_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED = "Periodic Energy Exported"; + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED = "electricalenergymeasurement-periodicenergyexported"; + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED); + + public static final String CHANNEL_NAME_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYRESET = "CumulativeEnergyReset"; + public static final String CHANNEL_LABEL_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYRESET = "Cumulative Energy Reset"; + public static final String CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYRESET = "electricalenergymeasurement-cumulativeenergyreset"; + public static final ChannelTypeUID CHANNEL_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYRESET = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYRESET); + + // ElectricalPowerMeasurement Cluster + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_POWERMODE = "PowerMode"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_POWERMODE = "Power Mode"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_POWERMODE = "electricalpowermeasurement-powermode"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_POWERMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_POWERMODE); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_NUMBEROFMEASUREMENTTYPES = "NumberOfMeasurementTypes"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_NUMBEROFMEASUREMENTTYPES = "Number Of Measurement Types"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_NUMBEROFMEASUREMENTTYPES = "electricalpowermeasurement-numberofmeasurementtypes"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_NUMBEROFMEASUREMENTTYPES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_NUMBEROFMEASUREMENTTYPES); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_ACCURACY = "Accuracy"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_ACCURACY = "Accuracy"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACCURACY = "electricalpowermeasurement-accuracy"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_ACCURACY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACCURACY); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_RANGES = "Ranges"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_RANGES = "Ranges"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RANGES = "electricalpowermeasurement-ranges"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_RANGES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RANGES); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_VOLTAGE = "Voltage"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_VOLTAGE = "Voltage"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE = "electricalpowermeasurement-voltage"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_VOLTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT = "ActiveCurrent"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT = "Active Current"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT = "electricalpowermeasurement-activecurrent"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_REACTIVECURRENT = "ReactiveCurrent"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_REACTIVECURRENT = "Reactive Current"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_REACTIVECURRENT = "electricalpowermeasurement-reactivecurrent"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_REACTIVECURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_REACTIVECURRENT); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_APPARENTCURRENT = "ApparentCurrent"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_APPARENTCURRENT = "Apparent Current"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_APPARENTCURRENT = "electricalpowermeasurement-apparentcurrent"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_APPARENTCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_APPARENTCURRENT); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER = "ActivePower"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER = "Active Power"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER = "electricalpowermeasurement-activepower"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_REACTIVEPOWER = "ReactivePower"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_REACTIVEPOWER = "Reactive Power"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_REACTIVEPOWER = "electricalpowermeasurement-reactivepower"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_REACTIVEPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_REACTIVEPOWER); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_APPARENTPOWER = "ApparentPower"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_APPARENTPOWER = "Apparent Power"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_APPARENTPOWER = "electricalpowermeasurement-apparentpower"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_APPARENTPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_APPARENTPOWER); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_RMSVOLTAGE = "RmsVoltage"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_RMSVOLTAGE = "Rms Voltage"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RMSVOLTAGE = "electricalpowermeasurement-rmsvoltage"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_RMSVOLTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RMSVOLTAGE); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_RMSCURRENT = "RmsCurrent"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_RMSCURRENT = "Rms Current"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RMSCURRENT = "electricalpowermeasurement-rmscurrent"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_RMSCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RMSCURRENT); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_RMSPOWER = "RmsPower"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_RMSPOWER = "Rms Power"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RMSPOWER = "electricalpowermeasurement-rmspower"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_RMSPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_RMSPOWER); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_FREQUENCY = "Frequency"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_FREQUENCY = "Frequency"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_FREQUENCY = "electricalpowermeasurement-frequency"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_FREQUENCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_FREQUENCY); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_HARMONICCURRENTS = "HarmonicCurrents"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_HARMONICCURRENTS = "Harmonic Currents"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_HARMONICCURRENTS = "electricalpowermeasurement-harmoniccurrents"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_HARMONICCURRENTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_HARMONICCURRENTS); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_HARMONICPHASES = "HarmonicPhases"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_HARMONICPHASES = "Harmonic Phases"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_HARMONICPHASES = "electricalpowermeasurement-harmonicphases"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_HARMONICPHASES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_HARMONICPHASES); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_POWERFACTOR = "PowerFactor"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_POWERFACTOR = "Power Factor"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_POWERFACTOR = "electricalpowermeasurement-powerfactor"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_POWERFACTOR = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_POWERFACTOR); + + public static final String CHANNEL_NAME_ELECTRICALPOWERMEASUREMENT_NEUTRALCURRENT = "NeutralCurrent"; + public static final String CHANNEL_LABEL_ELECTRICALPOWERMEASUREMENT_NEUTRALCURRENT = "Neutral Current"; + public static final String CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_NEUTRALCURRENT = "electricalpowermeasurement-neutralcurrent"; + public static final ChannelTypeUID CHANNEL_ELECTRICALPOWERMEASUREMENT_NEUTRALCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_NEUTRALCURRENT); + + // EnergyEvse Cluster + public static final String CHANNEL_NAME_ENERGYEVSE_STATE = "State"; + public static final String CHANNEL_LABEL_ENERGYEVSE_STATE = "State"; + public static final String CHANNEL_ID_ENERGYEVSE_STATE = "energyevse-state"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_STATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_STATE); + + public static final String CHANNEL_NAME_ENERGYEVSE_SUPPLYSTATE = "SupplyState"; + public static final String CHANNEL_LABEL_ENERGYEVSE_SUPPLYSTATE = "Supply State"; + public static final String CHANNEL_ID_ENERGYEVSE_SUPPLYSTATE = "energyevse-supplystate"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_SUPPLYSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_SUPPLYSTATE); + + public static final String CHANNEL_NAME_ENERGYEVSE_FAULTSTATE = "FaultState"; + public static final String CHANNEL_LABEL_ENERGYEVSE_FAULTSTATE = "Fault State"; + public static final String CHANNEL_ID_ENERGYEVSE_FAULTSTATE = "energyevse-faultstate"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_FAULTSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_FAULTSTATE); + + public static final String CHANNEL_NAME_ENERGYEVSE_CHARGINGENABLEDUNTIL = "ChargingEnabledUntil"; + public static final String CHANNEL_LABEL_ENERGYEVSE_CHARGINGENABLEDUNTIL = "Charging Enabled Until"; + public static final String CHANNEL_ID_ENERGYEVSE_CHARGINGENABLEDUNTIL = "energyevse-chargingenableduntil"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_CHARGINGENABLEDUNTIL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_CHARGINGENABLEDUNTIL); + + public static final String CHANNEL_NAME_ENERGYEVSE_DISCHARGINGENABLEDUNTIL = "DischargingEnabledUntil"; + public static final String CHANNEL_LABEL_ENERGYEVSE_DISCHARGINGENABLEDUNTIL = "Discharging Enabled Until"; + public static final String CHANNEL_ID_ENERGYEVSE_DISCHARGINGENABLEDUNTIL = "energyevse-dischargingenableduntil"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_DISCHARGINGENABLEDUNTIL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_DISCHARGINGENABLEDUNTIL); + + public static final String CHANNEL_NAME_ENERGYEVSE_CIRCUITCAPACITY = "CircuitCapacity"; + public static final String CHANNEL_LABEL_ENERGYEVSE_CIRCUITCAPACITY = "Circuit Capacity"; + public static final String CHANNEL_ID_ENERGYEVSE_CIRCUITCAPACITY = "energyevse-circuitcapacity"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_CIRCUITCAPACITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_CIRCUITCAPACITY); + + public static final String CHANNEL_NAME_ENERGYEVSE_MINIMUMCHARGECURRENT = "MinimumChargeCurrent"; + public static final String CHANNEL_LABEL_ENERGYEVSE_MINIMUMCHARGECURRENT = "Minimum Charge Current"; + public static final String CHANNEL_ID_ENERGYEVSE_MINIMUMCHARGECURRENT = "energyevse-minimumchargecurrent"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_MINIMUMCHARGECURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_MINIMUMCHARGECURRENT); + + public static final String CHANNEL_NAME_ENERGYEVSE_MAXIMUMCHARGECURRENT = "MaximumChargeCurrent"; + public static final String CHANNEL_LABEL_ENERGYEVSE_MAXIMUMCHARGECURRENT = "Maximum Charge Current"; + public static final String CHANNEL_ID_ENERGYEVSE_MAXIMUMCHARGECURRENT = "energyevse-maximumchargecurrent"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_MAXIMUMCHARGECURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_MAXIMUMCHARGECURRENT); + + public static final String CHANNEL_NAME_ENERGYEVSE_MAXIMUMDISCHARGECURRENT = "MaximumDischargeCurrent"; + public static final String CHANNEL_LABEL_ENERGYEVSE_MAXIMUMDISCHARGECURRENT = "Maximum Discharge Current"; + public static final String CHANNEL_ID_ENERGYEVSE_MAXIMUMDISCHARGECURRENT = "energyevse-maximumdischargecurrent"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_MAXIMUMDISCHARGECURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_MAXIMUMDISCHARGECURRENT); + + public static final String CHANNEL_NAME_ENERGYEVSE_USERMAXIMUMCHARGECURRENT = "UserMaximumChargeCurrent"; + public static final String CHANNEL_LABEL_ENERGYEVSE_USERMAXIMUMCHARGECURRENT = "User Maximum Charge Current"; + public static final String CHANNEL_ID_ENERGYEVSE_USERMAXIMUMCHARGECURRENT = "energyevse-usermaximumchargecurrent"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_USERMAXIMUMCHARGECURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_USERMAXIMUMCHARGECURRENT); + + public static final String CHANNEL_NAME_ENERGYEVSE_RANDOMIZATIONDELAYWINDOW = "RandomizationDelayWindow"; + public static final String CHANNEL_LABEL_ENERGYEVSE_RANDOMIZATIONDELAYWINDOW = "Randomization Delay Window"; + public static final String CHANNEL_ID_ENERGYEVSE_RANDOMIZATIONDELAYWINDOW = "energyevse-randomizationdelaywindow"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_RANDOMIZATIONDELAYWINDOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_RANDOMIZATIONDELAYWINDOW); + + public static final String CHANNEL_NAME_ENERGYEVSE_NEXTCHARGESTARTTIME = "NextChargeStartTime"; + public static final String CHANNEL_LABEL_ENERGYEVSE_NEXTCHARGESTARTTIME = "Next Charge Start Time"; + public static final String CHANNEL_ID_ENERGYEVSE_NEXTCHARGESTARTTIME = "energyevse-nextchargestarttime"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_NEXTCHARGESTARTTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_NEXTCHARGESTARTTIME); + + public static final String CHANNEL_NAME_ENERGYEVSE_NEXTCHARGETARGETTIME = "NextChargeTargetTime"; + public static final String CHANNEL_LABEL_ENERGYEVSE_NEXTCHARGETARGETTIME = "Next Charge Target Time"; + public static final String CHANNEL_ID_ENERGYEVSE_NEXTCHARGETARGETTIME = "energyevse-nextchargetargettime"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_NEXTCHARGETARGETTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_NEXTCHARGETARGETTIME); + + public static final String CHANNEL_NAME_ENERGYEVSE_NEXTCHARGEREQUIREDENERGY = "NextChargeRequiredEnergy"; + public static final String CHANNEL_LABEL_ENERGYEVSE_NEXTCHARGEREQUIREDENERGY = "Next Charge Required Energy"; + public static final String CHANNEL_ID_ENERGYEVSE_NEXTCHARGEREQUIREDENERGY = "energyevse-nextchargerequiredenergy"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_NEXTCHARGEREQUIREDENERGY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_NEXTCHARGEREQUIREDENERGY); + + public static final String CHANNEL_NAME_ENERGYEVSE_NEXTCHARGETARGETSOC = "NextChargeTargetSoC"; + public static final String CHANNEL_LABEL_ENERGYEVSE_NEXTCHARGETARGETSOC = "Next Charge Target So C"; + public static final String CHANNEL_ID_ENERGYEVSE_NEXTCHARGETARGETSOC = "energyevse-nextchargetargetsoc"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_NEXTCHARGETARGETSOC = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_NEXTCHARGETARGETSOC); + + public static final String CHANNEL_NAME_ENERGYEVSE_APPROXIMATEEVEFFICIENCY = "ApproximateEvEfficiency"; + public static final String CHANNEL_LABEL_ENERGYEVSE_APPROXIMATEEVEFFICIENCY = "Approximate Ev Efficiency"; + public static final String CHANNEL_ID_ENERGYEVSE_APPROXIMATEEVEFFICIENCY = "energyevse-approximateevefficiency"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_APPROXIMATEEVEFFICIENCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_APPROXIMATEEVEFFICIENCY); + + public static final String CHANNEL_NAME_ENERGYEVSE_STATEOFCHARGE = "StateOfCharge"; + public static final String CHANNEL_LABEL_ENERGYEVSE_STATEOFCHARGE = "State Of Charge"; + public static final String CHANNEL_ID_ENERGYEVSE_STATEOFCHARGE = "energyevse-stateofcharge"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_STATEOFCHARGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_STATEOFCHARGE); + + public static final String CHANNEL_NAME_ENERGYEVSE_BATTERYCAPACITY = "BatteryCapacity"; + public static final String CHANNEL_LABEL_ENERGYEVSE_BATTERYCAPACITY = "Battery Capacity"; + public static final String CHANNEL_ID_ENERGYEVSE_BATTERYCAPACITY = "energyevse-batterycapacity"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_BATTERYCAPACITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_BATTERYCAPACITY); + + public static final String CHANNEL_NAME_ENERGYEVSE_VEHICLEID = "VehicleId"; + public static final String CHANNEL_LABEL_ENERGYEVSE_VEHICLEID = "Vehicle Id"; + public static final String CHANNEL_ID_ENERGYEVSE_VEHICLEID = "energyevse-vehicleid"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_VEHICLEID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_VEHICLEID); + + public static final String CHANNEL_NAME_ENERGYEVSE_SESSIONID = "SessionId"; + public static final String CHANNEL_LABEL_ENERGYEVSE_SESSIONID = "Session Id"; + public static final String CHANNEL_ID_ENERGYEVSE_SESSIONID = "energyevse-sessionid"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_SESSIONID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_SESSIONID); + + public static final String CHANNEL_NAME_ENERGYEVSE_SESSIONDURATION = "SessionDuration"; + public static final String CHANNEL_LABEL_ENERGYEVSE_SESSIONDURATION = "Session Duration"; + public static final String CHANNEL_ID_ENERGYEVSE_SESSIONDURATION = "energyevse-sessionduration"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_SESSIONDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_SESSIONDURATION); + + public static final String CHANNEL_NAME_ENERGYEVSE_SESSIONENERGYCHARGED = "SessionEnergyCharged"; + public static final String CHANNEL_LABEL_ENERGYEVSE_SESSIONENERGYCHARGED = "Session Energy Charged"; + public static final String CHANNEL_ID_ENERGYEVSE_SESSIONENERGYCHARGED = "energyevse-sessionenergycharged"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_SESSIONENERGYCHARGED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_SESSIONENERGYCHARGED); + + public static final String CHANNEL_NAME_ENERGYEVSE_SESSIONENERGYDISCHARGED = "SessionEnergyDischarged"; + public static final String CHANNEL_LABEL_ENERGYEVSE_SESSIONENERGYDISCHARGED = "Session Energy Discharged"; + public static final String CHANNEL_ID_ENERGYEVSE_SESSIONENERGYDISCHARGED = "energyevse-sessionenergydischarged"; + public static final ChannelTypeUID CHANNEL_ENERGYEVSE_SESSIONENERGYDISCHARGED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYEVSE_SESSIONENERGYDISCHARGED); + + // EnergyEvseMode Cluster + // EnergyPreference Cluster + public static final String CHANNEL_NAME_ENERGYPREFERENCE_ENERGYBALANCES = "EnergyBalances"; + public static final String CHANNEL_LABEL_ENERGYPREFERENCE_ENERGYBALANCES = "Energy Balances"; + public static final String CHANNEL_ID_ENERGYPREFERENCE_ENERGYBALANCES = "energypreference-energybalances"; + public static final ChannelTypeUID CHANNEL_ENERGYPREFERENCE_ENERGYBALANCES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYPREFERENCE_ENERGYBALANCES); + + public static final String CHANNEL_NAME_ENERGYPREFERENCE_CURRENTENERGYBALANCE = "CurrentEnergyBalance"; + public static final String CHANNEL_LABEL_ENERGYPREFERENCE_CURRENTENERGYBALANCE = "Current Energy Balance"; + public static final String CHANNEL_ID_ENERGYPREFERENCE_CURRENTENERGYBALANCE = "energypreference-currentenergybalance"; + public static final ChannelTypeUID CHANNEL_ENERGYPREFERENCE_CURRENTENERGYBALANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYPREFERENCE_CURRENTENERGYBALANCE); + + public static final String CHANNEL_NAME_ENERGYPREFERENCE_ENERGYPRIORITIES = "EnergyPriorities"; + public static final String CHANNEL_LABEL_ENERGYPREFERENCE_ENERGYPRIORITIES = "Energy Priorities"; + public static final String CHANNEL_ID_ENERGYPREFERENCE_ENERGYPRIORITIES = "energypreference-energypriorities"; + public static final ChannelTypeUID CHANNEL_ENERGYPREFERENCE_ENERGYPRIORITIES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYPREFERENCE_ENERGYPRIORITIES); + + public static final String CHANNEL_NAME_ENERGYPREFERENCE_LOWPOWERMODESENSITIVITIES = "LowPowerModeSensitivities"; + public static final String CHANNEL_LABEL_ENERGYPREFERENCE_LOWPOWERMODESENSITIVITIES = "Low Power Mode Sensitivities"; + public static final String CHANNEL_ID_ENERGYPREFERENCE_LOWPOWERMODESENSITIVITIES = "energypreference-lowpowermodesensitivities"; + public static final ChannelTypeUID CHANNEL_ENERGYPREFERENCE_LOWPOWERMODESENSITIVITIES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYPREFERENCE_LOWPOWERMODESENSITIVITIES); + + public static final String CHANNEL_NAME_ENERGYPREFERENCE_CURRENTLOWPOWERMODESENSITIVITY = "CurrentLowPowerModeSensitivity"; + public static final String CHANNEL_LABEL_ENERGYPREFERENCE_CURRENTLOWPOWERMODESENSITIVITY = "Current Low Power Mode Sensitivity"; + public static final String CHANNEL_ID_ENERGYPREFERENCE_CURRENTLOWPOWERMODESENSITIVITY = "energypreference-currentlowpowermodesensitivity"; + public static final ChannelTypeUID CHANNEL_ENERGYPREFERENCE_CURRENTLOWPOWERMODESENSITIVITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ENERGYPREFERENCE_CURRENTLOWPOWERMODESENSITIVITY); + + // EthernetNetworkDiagnostics Cluster + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_PHYRATE = "PhyRate"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_PHYRATE = "Phy Rate"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_PHYRATE = "ethernetnetworkdiagnostics-phyrate"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_PHYRATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_PHYRATE); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_FULLDUPLEX = "FullDuplex"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_FULLDUPLEX = "Full Duplex"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_FULLDUPLEX = "ethernetnetworkdiagnostics-fullduplex"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_FULLDUPLEX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_FULLDUPLEX); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_PACKETRXCOUNT = "PacketRxCount"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_PACKETRXCOUNT = "Packet Rx Count"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_PACKETRXCOUNT = "ethernetnetworkdiagnostics-packetrxcount"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_PACKETRXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_PACKETRXCOUNT); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_PACKETTXCOUNT = "PacketTxCount"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_PACKETTXCOUNT = "Packet Tx Count"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_PACKETTXCOUNT = "ethernetnetworkdiagnostics-packettxcount"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_PACKETTXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_PACKETTXCOUNT); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_TXERRCOUNT = "TxErrCount"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_TXERRCOUNT = "Tx Err Count"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_TXERRCOUNT = "ethernetnetworkdiagnostics-txerrcount"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_TXERRCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_TXERRCOUNT); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_COLLISIONCOUNT = "CollisionCount"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_COLLISIONCOUNT = "Collision Count"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_COLLISIONCOUNT = "ethernetnetworkdiagnostics-collisioncount"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_COLLISIONCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_COLLISIONCOUNT); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_OVERRUNCOUNT = "OverrunCount"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_OVERRUNCOUNT = "Overrun Count"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_OVERRUNCOUNT = "ethernetnetworkdiagnostics-overruncount"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_OVERRUNCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_OVERRUNCOUNT); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_CARRIERDETECT = "CarrierDetect"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_CARRIERDETECT = "Carrier Detect"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_CARRIERDETECT = "ethernetnetworkdiagnostics-carrierdetect"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_CARRIERDETECT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_CARRIERDETECT); + + public static final String CHANNEL_NAME_ETHERNETNETWORKDIAGNOSTICS_TIMESINCERESET = "TimeSinceReset"; + public static final String CHANNEL_LABEL_ETHERNETNETWORKDIAGNOSTICS_TIMESINCERESET = "Time Since Reset"; + public static final String CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_TIMESINCERESET = "ethernetnetworkdiagnostics-timesincereset"; + public static final ChannelTypeUID CHANNEL_ETHERNETNETWORKDIAGNOSTICS_TIMESINCERESET = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ETHERNETNETWORKDIAGNOSTICS_TIMESINCERESET); + + // FanControl Cluster + public static final String CHANNEL_NAME_FANCONTROL_FANMODE = "FanMode"; + public static final String CHANNEL_LABEL_FANCONTROL_FANMODE = "Fan Mode"; + public static final String CHANNEL_ID_FANCONTROL_FANMODE = "fancontrol-fanmode"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_FANMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_FANMODE); + + public static final String CHANNEL_NAME_FANCONTROL_FANMODESEQUENCE = "FanModeSequence"; + public static final String CHANNEL_LABEL_FANCONTROL_FANMODESEQUENCE = "Fan Mode Sequence"; + public static final String CHANNEL_ID_FANCONTROL_FANMODESEQUENCE = "fancontrol-fanmodesequence"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_FANMODESEQUENCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_FANMODESEQUENCE); + + public static final String CHANNEL_NAME_FANCONTROL_PERCENTSETTING = "PercentSetting"; + public static final String CHANNEL_LABEL_FANCONTROL_PERCENTSETTING = "Percent Setting"; + public static final String CHANNEL_ID_FANCONTROL_PERCENTSETTING = "fancontrol-percentsetting"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_PERCENTSETTING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_PERCENTSETTING); + + public static final String CHANNEL_NAME_FANCONTROL_PERCENTCURRENT = "PercentCurrent"; + public static final String CHANNEL_LABEL_FANCONTROL_PERCENTCURRENT = "Percent Current"; + public static final String CHANNEL_ID_FANCONTROL_PERCENTCURRENT = "fancontrol-percentcurrent"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_PERCENTCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_PERCENTCURRENT); + + public static final String CHANNEL_NAME_FANCONTROL_SPEEDMAX = "SpeedMax"; + public static final String CHANNEL_LABEL_FANCONTROL_SPEEDMAX = "Speed Max"; + public static final String CHANNEL_ID_FANCONTROL_SPEEDMAX = "fancontrol-speedmax"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_SPEEDMAX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_SPEEDMAX); + + public static final String CHANNEL_NAME_FANCONTROL_SPEEDSETTING = "SpeedSetting"; + public static final String CHANNEL_LABEL_FANCONTROL_SPEEDSETTING = "Speed Setting"; + public static final String CHANNEL_ID_FANCONTROL_SPEEDSETTING = "fancontrol-speedsetting"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_SPEEDSETTING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_SPEEDSETTING); + + public static final String CHANNEL_NAME_FANCONTROL_SPEEDCURRENT = "SpeedCurrent"; + public static final String CHANNEL_LABEL_FANCONTROL_SPEEDCURRENT = "Speed Current"; + public static final String CHANNEL_ID_FANCONTROL_SPEEDCURRENT = "fancontrol-speedcurrent"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_SPEEDCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_SPEEDCURRENT); + + public static final String CHANNEL_NAME_FANCONTROL_ROCKSUPPORT = "RockSupport"; + public static final String CHANNEL_LABEL_FANCONTROL_ROCKSUPPORT = "Rock Support"; + public static final String CHANNEL_ID_FANCONTROL_ROCKSUPPORT = "fancontrol-rocksupport"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_ROCKSUPPORT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_ROCKSUPPORT); + + public static final String CHANNEL_NAME_FANCONTROL_ROCKSETTING = "RockSetting"; + public static final String CHANNEL_LABEL_FANCONTROL_ROCKSETTING = "Rock Setting"; + public static final String CHANNEL_ID_FANCONTROL_ROCKSETTING = "fancontrol-rocksetting"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_ROCKSETTING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_ROCKSETTING); + + public static final String CHANNEL_NAME_FANCONTROL_WINDSUPPORT = "WindSupport"; + public static final String CHANNEL_LABEL_FANCONTROL_WINDSUPPORT = "Wind Support"; + public static final String CHANNEL_ID_FANCONTROL_WINDSUPPORT = "fancontrol-windsupport"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_WINDSUPPORT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_WINDSUPPORT); + + public static final String CHANNEL_NAME_FANCONTROL_WINDSETTING = "WindSetting"; + public static final String CHANNEL_LABEL_FANCONTROL_WINDSETTING = "Wind Setting"; + public static final String CHANNEL_ID_FANCONTROL_WINDSETTING = "fancontrol-windsetting"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_WINDSETTING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_WINDSETTING); + + public static final String CHANNEL_NAME_FANCONTROL_AIRFLOWDIRECTION = "AirflowDirection"; + public static final String CHANNEL_LABEL_FANCONTROL_AIRFLOWDIRECTION = "Airflow Direction"; + public static final String CHANNEL_ID_FANCONTROL_AIRFLOWDIRECTION = "fancontrol-airflowdirection"; + public static final ChannelTypeUID CHANNEL_FANCONTROL_AIRFLOWDIRECTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FANCONTROL_AIRFLOWDIRECTION); + + // FixedLabel Cluster + public static final String CHANNEL_NAME_FIXEDLABEL_LABELLIST = "LabelList"; + public static final String CHANNEL_LABEL_FIXEDLABEL_LABELLIST = "Label List"; + public static final String CHANNEL_ID_FIXEDLABEL_LABELLIST = "fixedlabel-labellist"; + public static final ChannelTypeUID CHANNEL_FIXEDLABEL_LABELLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FIXEDLABEL_LABELLIST); + + // FlowMeasurement Cluster + public static final String CHANNEL_NAME_FLOWMEASUREMENT_MEASUREDVALUE = "MeasuredValue"; + public static final String CHANNEL_LABEL_FLOWMEASUREMENT_MEASUREDVALUE = "Measured Value"; + public static final String CHANNEL_ID_FLOWMEASUREMENT_MEASUREDVALUE = "flowmeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_FLOWMEASUREMENT_MEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FLOWMEASUREMENT_MEASUREDVALUE); + + public static final String CHANNEL_NAME_FLOWMEASUREMENT_MINMEASUREDVALUE = "MinMeasuredValue"; + public static final String CHANNEL_LABEL_FLOWMEASUREMENT_MINMEASUREDVALUE = "Min Measured Value"; + public static final String CHANNEL_ID_FLOWMEASUREMENT_MINMEASUREDVALUE = "flowmeasurement-minmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_FLOWMEASUREMENT_MINMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FLOWMEASUREMENT_MINMEASUREDVALUE); + + public static final String CHANNEL_NAME_FLOWMEASUREMENT_MAXMEASUREDVALUE = "MaxMeasuredValue"; + public static final String CHANNEL_LABEL_FLOWMEASUREMENT_MAXMEASUREDVALUE = "Max Measured Value"; + public static final String CHANNEL_ID_FLOWMEASUREMENT_MAXMEASUREDVALUE = "flowmeasurement-maxmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_FLOWMEASUREMENT_MAXMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FLOWMEASUREMENT_MAXMEASUREDVALUE); + + public static final String CHANNEL_NAME_FLOWMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_LABEL_FLOWMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_ID_FLOWMEASUREMENT_TOLERANCE = "flowmeasurement-tolerance"; + public static final ChannelTypeUID CHANNEL_FLOWMEASUREMENT_TOLERANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_FLOWMEASUREMENT_TOLERANCE); + + // FormaldehydeConcentrationMeasurement Cluster + // GeneralCommissioning Cluster + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_BREADCRUMB = "Breadcrumb"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_BREADCRUMB = "Breadcrumb"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_BREADCRUMB = "generalcommissioning-breadcrumb"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_BREADCRUMB = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_BREADCRUMB); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_BASICCOMMISSIONINGINFO = "BasicCommissioningInfo"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_BASICCOMMISSIONINGINFO = "Basic Commissioning Info"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_BASICCOMMISSIONINGINFO = "generalcommissioning-basiccommissioninginfo"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_BASICCOMMISSIONINGINFO = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_BASICCOMMISSIONINGINFO); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_REGULATORYCONFIG = "RegulatoryConfig"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_REGULATORYCONFIG = "Regulatory Config"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_REGULATORYCONFIG = "generalcommissioning-regulatoryconfig"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_REGULATORYCONFIG = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_REGULATORYCONFIG); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_LOCATIONCAPABILITY = "LocationCapability"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_LOCATIONCAPABILITY = "Location Capability"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_LOCATIONCAPABILITY = "generalcommissioning-locationcapability"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_LOCATIONCAPABILITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_LOCATIONCAPABILITY); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_SUPPORTSCONCURRENTCONNECTION = "SupportsConcurrentConnection"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_SUPPORTSCONCURRENTCONNECTION = "Supports Concurrent Connection"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_SUPPORTSCONCURRENTCONNECTION = "generalcommissioning-supportsconcurrentconnection"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_SUPPORTSCONCURRENTCONNECTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_SUPPORTSCONCURRENTCONNECTION); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_TCACCEPTEDVERSION = "TcAcceptedVersion"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_TCACCEPTEDVERSION = "Tc Accepted Version"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_TCACCEPTEDVERSION = "generalcommissioning-tcacceptedversion"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_TCACCEPTEDVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_TCACCEPTEDVERSION); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_TCMINREQUIREDVERSION = "TcMinRequiredVersion"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_TCMINREQUIREDVERSION = "Tc Min Required Version"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_TCMINREQUIREDVERSION = "generalcommissioning-tcminrequiredversion"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_TCMINREQUIREDVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_TCMINREQUIREDVERSION); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTS = "TcAcknowledgements"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTS = "Tc Acknowledgements"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTS = "generalcommissioning-tcacknowledgements"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTS); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTSREQUIRED = "TcAcknowledgementsRequired"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTSREQUIRED = "Tc Acknowledgements Required"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTSREQUIRED = "generalcommissioning-tcacknowledgementsrequired"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTSREQUIRED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_TCACKNOWLEDGEMENTSREQUIRED); + + public static final String CHANNEL_NAME_GENERALCOMMISSIONING_TCUPDATEDEADLINE = "TcUpdateDeadline"; + public static final String CHANNEL_LABEL_GENERALCOMMISSIONING_TCUPDATEDEADLINE = "Tc Update Deadline"; + public static final String CHANNEL_ID_GENERALCOMMISSIONING_TCUPDATEDEADLINE = "generalcommissioning-tcupdatedeadline"; + public static final ChannelTypeUID CHANNEL_GENERALCOMMISSIONING_TCUPDATEDEADLINE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALCOMMISSIONING_TCUPDATEDEADLINE); + + // GeneralDiagnostics Cluster + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_NETWORKINTERFACES = "NetworkInterfaces"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_NETWORKINTERFACES = "Network Interfaces"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_NETWORKINTERFACES = "generaldiagnostics-networkinterfaces"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_NETWORKINTERFACES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_NETWORKINTERFACES); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_REBOOTCOUNT = "RebootCount"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_REBOOTCOUNT = "Reboot Count"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_REBOOTCOUNT = "generaldiagnostics-rebootcount"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_REBOOTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_REBOOTCOUNT); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_UPTIME = "UpTime"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_UPTIME = "Up Time"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_UPTIME = "generaldiagnostics-uptime"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_UPTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_UPTIME); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_TOTALOPERATIONALHOURS = "TotalOperationalHours"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_TOTALOPERATIONALHOURS = "Total Operational Hours"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_TOTALOPERATIONALHOURS = "generaldiagnostics-totaloperationalhours"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_TOTALOPERATIONALHOURS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_TOTALOPERATIONALHOURS); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_BOOTREASON = "BootReason"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_BOOTREASON = "Boot Reason"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_BOOTREASON = "generaldiagnostics-bootreason"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_BOOTREASON = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_BOOTREASON); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_ACTIVEHARDWAREFAULTS = "ActiveHardwareFaults"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_ACTIVEHARDWAREFAULTS = "Active Hardware Faults"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_ACTIVEHARDWAREFAULTS = "generaldiagnostics-activehardwarefaults"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_ACTIVEHARDWAREFAULTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_ACTIVEHARDWAREFAULTS); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_ACTIVERADIOFAULTS = "ActiveRadioFaults"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_ACTIVERADIOFAULTS = "Active Radio Faults"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_ACTIVERADIOFAULTS = "generaldiagnostics-activeradiofaults"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_ACTIVERADIOFAULTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_ACTIVERADIOFAULTS); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_ACTIVENETWORKFAULTS = "ActiveNetworkFaults"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_ACTIVENETWORKFAULTS = "Active Network Faults"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_ACTIVENETWORKFAULTS = "generaldiagnostics-activenetworkfaults"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_ACTIVENETWORKFAULTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_ACTIVENETWORKFAULTS); + + public static final String CHANNEL_NAME_GENERALDIAGNOSTICS_TESTEVENTTRIGGERSENABLED = "TestEventTriggersEnabled"; + public static final String CHANNEL_LABEL_GENERALDIAGNOSTICS_TESTEVENTTRIGGERSENABLED = "Test Event Triggers Enabled"; + public static final String CHANNEL_ID_GENERALDIAGNOSTICS_TESTEVENTTRIGGERSENABLED = "generaldiagnostics-testeventtriggersenabled"; + public static final ChannelTypeUID CHANNEL_GENERALDIAGNOSTICS_TESTEVENTTRIGGERSENABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GENERALDIAGNOSTICS_TESTEVENTTRIGGERSENABLED); + + // GroupKeyManagement Cluster + public static final String CHANNEL_NAME_GROUPKEYMANAGEMENT_GROUPKEYMAP = "GroupKeyMap"; + public static final String CHANNEL_LABEL_GROUPKEYMANAGEMENT_GROUPKEYMAP = "Group Key Map"; + public static final String CHANNEL_ID_GROUPKEYMANAGEMENT_GROUPKEYMAP = "groupkeymanagement-groupkeymap"; + public static final ChannelTypeUID CHANNEL_GROUPKEYMANAGEMENT_GROUPKEYMAP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GROUPKEYMANAGEMENT_GROUPKEYMAP); + + public static final String CHANNEL_NAME_GROUPKEYMANAGEMENT_GROUPTABLE = "GroupTable"; + public static final String CHANNEL_LABEL_GROUPKEYMANAGEMENT_GROUPTABLE = "Group Table"; + public static final String CHANNEL_ID_GROUPKEYMANAGEMENT_GROUPTABLE = "groupkeymanagement-grouptable"; + public static final ChannelTypeUID CHANNEL_GROUPKEYMANAGEMENT_GROUPTABLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GROUPKEYMANAGEMENT_GROUPTABLE); + + public static final String CHANNEL_NAME_GROUPKEYMANAGEMENT_MAXGROUPSPERFABRIC = "MaxGroupsPerFabric"; + public static final String CHANNEL_LABEL_GROUPKEYMANAGEMENT_MAXGROUPSPERFABRIC = "Max Groups Per Fabric"; + public static final String CHANNEL_ID_GROUPKEYMANAGEMENT_MAXGROUPSPERFABRIC = "groupkeymanagement-maxgroupsperfabric"; + public static final ChannelTypeUID CHANNEL_GROUPKEYMANAGEMENT_MAXGROUPSPERFABRIC = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GROUPKEYMANAGEMENT_MAXGROUPSPERFABRIC); + + public static final String CHANNEL_NAME_GROUPKEYMANAGEMENT_MAXGROUPKEYSPERFABRIC = "MaxGroupKeysPerFabric"; + public static final String CHANNEL_LABEL_GROUPKEYMANAGEMENT_MAXGROUPKEYSPERFABRIC = "Max Group Keys Per Fabric"; + public static final String CHANNEL_ID_GROUPKEYMANAGEMENT_MAXGROUPKEYSPERFABRIC = "groupkeymanagement-maxgroupkeysperfabric"; + public static final ChannelTypeUID CHANNEL_GROUPKEYMANAGEMENT_MAXGROUPKEYSPERFABRIC = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GROUPKEYMANAGEMENT_MAXGROUPKEYSPERFABRIC); + + // Groups Cluster + public static final String CHANNEL_NAME_GROUPS_NAMESUPPORT = "NameSupport"; + public static final String CHANNEL_LABEL_GROUPS_NAMESUPPORT = "Name Support"; + public static final String CHANNEL_ID_GROUPS_NAMESUPPORT = "groups-namesupport"; + public static final ChannelTypeUID CHANNEL_GROUPS_NAMESUPPORT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_GROUPS_NAMESUPPORT); + + // HepaFilterMonitoring Cluster + // IcdManagement Cluster + public static final String CHANNEL_NAME_ICDMANAGEMENT_IDLEMODEDURATION = "IdleModeDuration"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_IDLEMODEDURATION = "Idle Mode Duration"; + public static final String CHANNEL_ID_ICDMANAGEMENT_IDLEMODEDURATION = "icdmanagement-idlemodeduration"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_IDLEMODEDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_IDLEMODEDURATION); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_ACTIVEMODEDURATION = "ActiveModeDuration"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_ACTIVEMODEDURATION = "Active Mode Duration"; + public static final String CHANNEL_ID_ICDMANAGEMENT_ACTIVEMODEDURATION = "icdmanagement-activemodeduration"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_ACTIVEMODEDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_ACTIVEMODEDURATION); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_ACTIVEMODETHRESHOLD = "ActiveModeThreshold"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_ACTIVEMODETHRESHOLD = "Active Mode Threshold"; + public static final String CHANNEL_ID_ICDMANAGEMENT_ACTIVEMODETHRESHOLD = "icdmanagement-activemodethreshold"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_ACTIVEMODETHRESHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_ACTIVEMODETHRESHOLD); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_REGISTEREDCLIENTS = "RegisteredClients"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_REGISTEREDCLIENTS = "Registered Clients"; + public static final String CHANNEL_ID_ICDMANAGEMENT_REGISTEREDCLIENTS = "icdmanagement-registeredclients"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_REGISTEREDCLIENTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_REGISTEREDCLIENTS); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_ICDCOUNTER = "IcdCounter"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_ICDCOUNTER = "Icd Counter"; + public static final String CHANNEL_ID_ICDMANAGEMENT_ICDCOUNTER = "icdmanagement-icdcounter"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_ICDCOUNTER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_ICDCOUNTER); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_CLIENTSSUPPORTEDPERFABRIC = "ClientsSupportedPerFabric"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_CLIENTSSUPPORTEDPERFABRIC = "Clients Supported Per Fabric"; + public static final String CHANNEL_ID_ICDMANAGEMENT_CLIENTSSUPPORTEDPERFABRIC = "icdmanagement-clientssupportedperfabric"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_CLIENTSSUPPORTEDPERFABRIC = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_CLIENTSSUPPORTEDPERFABRIC); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_USERACTIVEMODETRIGGERHINT = "UserActiveModeTriggerHint"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_USERACTIVEMODETRIGGERHINT = "User Active Mode Trigger Hint"; + public static final String CHANNEL_ID_ICDMANAGEMENT_USERACTIVEMODETRIGGERHINT = "icdmanagement-useractivemodetriggerhint"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_USERACTIVEMODETRIGGERHINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_USERACTIVEMODETRIGGERHINT); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_USERACTIVEMODETRIGGERINSTRUCTION = "UserActiveModeTriggerInstruction"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_USERACTIVEMODETRIGGERINSTRUCTION = "User Active Mode Trigger Instruction"; + public static final String CHANNEL_ID_ICDMANAGEMENT_USERACTIVEMODETRIGGERINSTRUCTION = "icdmanagement-useractivemodetriggerinstruction"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_USERACTIVEMODETRIGGERINSTRUCTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_USERACTIVEMODETRIGGERINSTRUCTION); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_OPERATINGMODE = "OperatingMode"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_OPERATINGMODE = "Operating Mode"; + public static final String CHANNEL_ID_ICDMANAGEMENT_OPERATINGMODE = "icdmanagement-operatingmode"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_OPERATINGMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_OPERATINGMODE); + + public static final String CHANNEL_NAME_ICDMANAGEMENT_MAXIMUMCHECKINBACKOFF = "MaximumCheckInBackoff"; + public static final String CHANNEL_LABEL_ICDMANAGEMENT_MAXIMUMCHECKINBACKOFF = "Maximum Check In Backoff"; + public static final String CHANNEL_ID_ICDMANAGEMENT_MAXIMUMCHECKINBACKOFF = "icdmanagement-maximumcheckinbackoff"; + public static final ChannelTypeUID CHANNEL_ICDMANAGEMENT_MAXIMUMCHECKINBACKOFF = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ICDMANAGEMENT_MAXIMUMCHECKINBACKOFF); + + // Identify Cluster + public static final String CHANNEL_NAME_IDENTIFY_IDENTIFYTIME = "IdentifyTime"; + public static final String CHANNEL_LABEL_IDENTIFY_IDENTIFYTIME = "Identify Time"; + public static final String CHANNEL_ID_IDENTIFY_IDENTIFYTIME = "identify-identifytime"; + public static final ChannelTypeUID CHANNEL_IDENTIFY_IDENTIFYTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_IDENTIFY_IDENTIFYTIME); + + public static final String CHANNEL_NAME_IDENTIFY_IDENTIFYTYPE = "IdentifyType"; + public static final String CHANNEL_LABEL_IDENTIFY_IDENTIFYTYPE = "Identify Type"; + public static final String CHANNEL_ID_IDENTIFY_IDENTIFYTYPE = "identify-identifytype"; + public static final ChannelTypeUID CHANNEL_IDENTIFY_IDENTIFYTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_IDENTIFY_IDENTIFYTYPE); + + // IlluminanceMeasurement Cluster + public static final String CHANNEL_NAME_ILLUMINANCEMEASUREMENT_MEASUREDVALUE = "MeasuredValue"; + public static final String CHANNEL_LABEL_ILLUMINANCEMEASUREMENT_MEASUREDVALUE = "Measured Value"; + public static final String CHANNEL_ID_ILLUMINANCEMEASUREMENT_MEASUREDVALUE = "illuminancemeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_ILLUMINANCEMEASUREMENT_MEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ILLUMINANCEMEASUREMENT_MEASUREDVALUE); + + public static final String CHANNEL_NAME_ILLUMINANCEMEASUREMENT_MINMEASUREDVALUE = "MinMeasuredValue"; + public static final String CHANNEL_LABEL_ILLUMINANCEMEASUREMENT_MINMEASUREDVALUE = "Min Measured Value"; + public static final String CHANNEL_ID_ILLUMINANCEMEASUREMENT_MINMEASUREDVALUE = "illuminancemeasurement-minmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_ILLUMINANCEMEASUREMENT_MINMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ILLUMINANCEMEASUREMENT_MINMEASUREDVALUE); + + public static final String CHANNEL_NAME_ILLUMINANCEMEASUREMENT_MAXMEASUREDVALUE = "MaxMeasuredValue"; + public static final String CHANNEL_LABEL_ILLUMINANCEMEASUREMENT_MAXMEASUREDVALUE = "Max Measured Value"; + public static final String CHANNEL_ID_ILLUMINANCEMEASUREMENT_MAXMEASUREDVALUE = "illuminancemeasurement-maxmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_ILLUMINANCEMEASUREMENT_MAXMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ILLUMINANCEMEASUREMENT_MAXMEASUREDVALUE); + + public static final String CHANNEL_NAME_ILLUMINANCEMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_LABEL_ILLUMINANCEMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_ID_ILLUMINANCEMEASUREMENT_TOLERANCE = "illuminancemeasurement-tolerance"; + public static final ChannelTypeUID CHANNEL_ILLUMINANCEMEASUREMENT_TOLERANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ILLUMINANCEMEASUREMENT_TOLERANCE); + + public static final String CHANNEL_NAME_ILLUMINANCEMEASUREMENT_LIGHTSENSORTYPE = "LightSensorType"; + public static final String CHANNEL_LABEL_ILLUMINANCEMEASUREMENT_LIGHTSENSORTYPE = "Light Sensor Type"; + public static final String CHANNEL_ID_ILLUMINANCEMEASUREMENT_LIGHTSENSORTYPE = "illuminancemeasurement-lightsensortype"; + public static final ChannelTypeUID CHANNEL_ILLUMINANCEMEASUREMENT_LIGHTSENSORTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ILLUMINANCEMEASUREMENT_LIGHTSENSORTYPE); + + // JointFabricDatastore Cluster + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_ANCHORROOTCA = "AnchorRootCa"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_ANCHORROOTCA = "Anchor Root Ca"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_ANCHORROOTCA = "jointfabricdatastore-anchorrootca"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_ANCHORROOTCA = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_ANCHORROOTCA); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_ANCHORNODEID = "AnchorNodeId"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_ANCHORNODEID = "Anchor Node Id"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_ANCHORNODEID = "jointfabricdatastore-anchornodeid"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_ANCHORNODEID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_ANCHORNODEID); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_ANCHORVENDORID = "AnchorVendorId"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_ANCHORVENDORID = "Anchor Vendor Id"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_ANCHORVENDORID = "jointfabricdatastore-anchorvendorid"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_ANCHORVENDORID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_ANCHORVENDORID); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_FRIENDLYNAME = "FriendlyName"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_FRIENDLYNAME = "Friendly Name"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_FRIENDLYNAME = "jointfabricdatastore-friendlyname"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_FRIENDLYNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_FRIENDLYNAME); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_GROUPKEYSETLIST = "GroupKeySetList"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_GROUPKEYSETLIST = "Group Key Set List"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_GROUPKEYSETLIST = "jointfabricdatastore-groupkeysetlist"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_GROUPKEYSETLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_GROUPKEYSETLIST); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_GROUPLIST = "GroupList"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_GROUPLIST = "Group List"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_GROUPLIST = "jointfabricdatastore-grouplist"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_GROUPLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_GROUPLIST); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_NODELIST = "NodeList"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_NODELIST = "Node List"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_NODELIST = "jointfabricdatastore-nodelist"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_NODELIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_NODELIST); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_ADMINLIST = "AdminList"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_ADMINLIST = "Admin List"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_ADMINLIST = "jointfabricdatastore-adminlist"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_ADMINLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_ADMINLIST); + + public static final String CHANNEL_NAME_JOINTFABRICDATASTORE_STATUSENTRY = "StatusEntry"; + public static final String CHANNEL_LABEL_JOINTFABRICDATASTORE_STATUSENTRY = "Status Entry"; + public static final String CHANNEL_ID_JOINTFABRICDATASTORE_STATUSENTRY = "jointfabricdatastore-statusentry"; + public static final ChannelTypeUID CHANNEL_JOINTFABRICDATASTORE_STATUSENTRY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_JOINTFABRICDATASTORE_STATUSENTRY); + + // JointFabricPki Cluster + // KeypadInput Cluster + // Label Cluster + // LaundryDryerControls Cluster + public static final String CHANNEL_NAME_LAUNDRYDRYERCONTROLS_SUPPORTEDDRYNESSLEVELS = "SupportedDrynessLevels"; + public static final String CHANNEL_LABEL_LAUNDRYDRYERCONTROLS_SUPPORTEDDRYNESSLEVELS = "Supported Dryness Levels"; + public static final String CHANNEL_ID_LAUNDRYDRYERCONTROLS_SUPPORTEDDRYNESSLEVELS = "laundrydryercontrols-supporteddrynesslevels"; + public static final ChannelTypeUID CHANNEL_LAUNDRYDRYERCONTROLS_SUPPORTEDDRYNESSLEVELS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LAUNDRYDRYERCONTROLS_SUPPORTEDDRYNESSLEVELS); + + public static final String CHANNEL_NAME_LAUNDRYDRYERCONTROLS_SELECTEDDRYNESSLEVEL = "SelectedDrynessLevel"; + public static final String CHANNEL_LABEL_LAUNDRYDRYERCONTROLS_SELECTEDDRYNESSLEVEL = "Selected Dryness Level"; + public static final String CHANNEL_ID_LAUNDRYDRYERCONTROLS_SELECTEDDRYNESSLEVEL = "laundrydryercontrols-selecteddrynesslevel"; + public static final ChannelTypeUID CHANNEL_LAUNDRYDRYERCONTROLS_SELECTEDDRYNESSLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LAUNDRYDRYERCONTROLS_SELECTEDDRYNESSLEVEL); + + // LaundryWasherControls Cluster + public static final String CHANNEL_NAME_LAUNDRYWASHERCONTROLS_SPINSPEEDS = "SpinSpeeds"; + public static final String CHANNEL_LABEL_LAUNDRYWASHERCONTROLS_SPINSPEEDS = "Spin Speeds"; + public static final String CHANNEL_ID_LAUNDRYWASHERCONTROLS_SPINSPEEDS = "laundrywashercontrols-spinspeeds"; + public static final ChannelTypeUID CHANNEL_LAUNDRYWASHERCONTROLS_SPINSPEEDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LAUNDRYWASHERCONTROLS_SPINSPEEDS); + + public static final String CHANNEL_NAME_LAUNDRYWASHERCONTROLS_SPINSPEEDCURRENT = "SpinSpeedCurrent"; + public static final String CHANNEL_LABEL_LAUNDRYWASHERCONTROLS_SPINSPEEDCURRENT = "Spin Speed Current"; + public static final String CHANNEL_ID_LAUNDRYWASHERCONTROLS_SPINSPEEDCURRENT = "laundrywashercontrols-spinspeedcurrent"; + public static final ChannelTypeUID CHANNEL_LAUNDRYWASHERCONTROLS_SPINSPEEDCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LAUNDRYWASHERCONTROLS_SPINSPEEDCURRENT); + + public static final String CHANNEL_NAME_LAUNDRYWASHERCONTROLS_NUMBEROFRINSES = "NumberOfRinses"; + public static final String CHANNEL_LABEL_LAUNDRYWASHERCONTROLS_NUMBEROFRINSES = "Number Of Rinses"; + public static final String CHANNEL_ID_LAUNDRYWASHERCONTROLS_NUMBEROFRINSES = "laundrywashercontrols-numberofrinses"; + public static final ChannelTypeUID CHANNEL_LAUNDRYWASHERCONTROLS_NUMBEROFRINSES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LAUNDRYWASHERCONTROLS_NUMBEROFRINSES); + + public static final String CHANNEL_NAME_LAUNDRYWASHERCONTROLS_SUPPORTEDRINSES = "SupportedRinses"; + public static final String CHANNEL_LABEL_LAUNDRYWASHERCONTROLS_SUPPORTEDRINSES = "Supported Rinses"; + public static final String CHANNEL_ID_LAUNDRYWASHERCONTROLS_SUPPORTEDRINSES = "laundrywashercontrols-supportedrinses"; + public static final ChannelTypeUID CHANNEL_LAUNDRYWASHERCONTROLS_SUPPORTEDRINSES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LAUNDRYWASHERCONTROLS_SUPPORTEDRINSES); + + // LaundryWasherMode Cluster + // LevelControl Cluster + public static final String CHANNEL_NAME_LEVELCONTROL_CURRENTLEVEL = "CurrentLevel"; + public static final String CHANNEL_LABEL_LEVELCONTROL_CURRENTLEVEL = "Current Level"; + public static final String CHANNEL_ID_LEVELCONTROL_CURRENTLEVEL = "levelcontrol-currentlevel"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_CURRENTLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_CURRENTLEVEL); + + public static final String CHANNEL_NAME_LEVELCONTROL_REMAININGTIME = "RemainingTime"; + public static final String CHANNEL_LABEL_LEVELCONTROL_REMAININGTIME = "Remaining Time"; + public static final String CHANNEL_ID_LEVELCONTROL_REMAININGTIME = "levelcontrol-remainingtime"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_REMAININGTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_REMAININGTIME); + + public static final String CHANNEL_NAME_LEVELCONTROL_MINLEVEL = "MinLevel"; + public static final String CHANNEL_LABEL_LEVELCONTROL_MINLEVEL = "Min Level"; + public static final String CHANNEL_ID_LEVELCONTROL_MINLEVEL = "levelcontrol-minlevel"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_MINLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_MINLEVEL); + + public static final String CHANNEL_NAME_LEVELCONTROL_MAXLEVEL = "MaxLevel"; + public static final String CHANNEL_LABEL_LEVELCONTROL_MAXLEVEL = "Max Level"; + public static final String CHANNEL_ID_LEVELCONTROL_MAXLEVEL = "levelcontrol-maxlevel"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_MAXLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_MAXLEVEL); + + public static final String CHANNEL_NAME_LEVELCONTROL_CURRENTFREQUENCY = "CurrentFrequency"; + public static final String CHANNEL_LABEL_LEVELCONTROL_CURRENTFREQUENCY = "Current Frequency"; + public static final String CHANNEL_ID_LEVELCONTROL_CURRENTFREQUENCY = "levelcontrol-currentfrequency"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_CURRENTFREQUENCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_CURRENTFREQUENCY); + + public static final String CHANNEL_NAME_LEVELCONTROL_MINFREQUENCY = "MinFrequency"; + public static final String CHANNEL_LABEL_LEVELCONTROL_MINFREQUENCY = "Min Frequency"; + public static final String CHANNEL_ID_LEVELCONTROL_MINFREQUENCY = "levelcontrol-minfrequency"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_MINFREQUENCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_MINFREQUENCY); + + public static final String CHANNEL_NAME_LEVELCONTROL_MAXFREQUENCY = "MaxFrequency"; + public static final String CHANNEL_LABEL_LEVELCONTROL_MAXFREQUENCY = "Max Frequency"; + public static final String CHANNEL_ID_LEVELCONTROL_MAXFREQUENCY = "levelcontrol-maxfrequency"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_MAXFREQUENCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_MAXFREQUENCY); + + public static final String CHANNEL_NAME_LEVELCONTROL_ONOFFTRANSITIONTIME = "OnOffTransitionTime"; + public static final String CHANNEL_LABEL_LEVELCONTROL_ONOFFTRANSITIONTIME = "On Off Transition Time"; + public static final String CHANNEL_ID_LEVELCONTROL_ONOFFTRANSITIONTIME = "levelcontrol-onofftransitiontime"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_ONOFFTRANSITIONTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_ONOFFTRANSITIONTIME); + + public static final String CHANNEL_NAME_LEVELCONTROL_ONLEVEL = "OnLevel"; + public static final String CHANNEL_LABEL_LEVELCONTROL_ONLEVEL = "On Level"; + public static final String CHANNEL_ID_LEVELCONTROL_ONLEVEL = "levelcontrol-onlevel"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_ONLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_ONLEVEL); + + public static final String CHANNEL_NAME_LEVELCONTROL_ONTRANSITIONTIME = "OnTransitionTime"; + public static final String CHANNEL_LABEL_LEVELCONTROL_ONTRANSITIONTIME = "On Transition Time"; + public static final String CHANNEL_ID_LEVELCONTROL_ONTRANSITIONTIME = "levelcontrol-ontransitiontime"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_ONTRANSITIONTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_ONTRANSITIONTIME); + + public static final String CHANNEL_NAME_LEVELCONTROL_OFFTRANSITIONTIME = "OffTransitionTime"; + public static final String CHANNEL_LABEL_LEVELCONTROL_OFFTRANSITIONTIME = "Off Transition Time"; + public static final String CHANNEL_ID_LEVELCONTROL_OFFTRANSITIONTIME = "levelcontrol-offtransitiontime"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_OFFTRANSITIONTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_OFFTRANSITIONTIME); + + public static final String CHANNEL_NAME_LEVELCONTROL_DEFAULTMOVERATE = "DefaultMoveRate"; + public static final String CHANNEL_LABEL_LEVELCONTROL_DEFAULTMOVERATE = "Default Move Rate"; + public static final String CHANNEL_ID_LEVELCONTROL_DEFAULTMOVERATE = "levelcontrol-defaultmoverate"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_DEFAULTMOVERATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_DEFAULTMOVERATE); + + public static final String CHANNEL_NAME_LEVELCONTROL_OPTIONS = "Options"; + public static final String CHANNEL_LABEL_LEVELCONTROL_OPTIONS = "Options"; + public static final String CHANNEL_ID_LEVELCONTROL_OPTIONS = "levelcontrol-options"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_OPTIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_OPTIONS); + + public static final String CHANNEL_NAME_LEVELCONTROL_STARTUPCURRENTLEVEL = "StartUpCurrentLevel"; + public static final String CHANNEL_LABEL_LEVELCONTROL_STARTUPCURRENTLEVEL = "Start Up Current Level"; + public static final String CHANNEL_ID_LEVELCONTROL_STARTUPCURRENTLEVEL = "levelcontrol-startupcurrentlevel"; + public static final ChannelTypeUID CHANNEL_LEVELCONTROL_STARTUPCURRENTLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LEVELCONTROL_STARTUPCURRENTLEVEL); + + // LocalizationConfiguration Cluster + public static final String CHANNEL_NAME_LOCALIZATIONCONFIGURATION_ACTIVELOCALE = "ActiveLocale"; + public static final String CHANNEL_LABEL_LOCALIZATIONCONFIGURATION_ACTIVELOCALE = "Active Locale"; + public static final String CHANNEL_ID_LOCALIZATIONCONFIGURATION_ACTIVELOCALE = "localizationconfiguration-activelocale"; + public static final ChannelTypeUID CHANNEL_LOCALIZATIONCONFIGURATION_ACTIVELOCALE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LOCALIZATIONCONFIGURATION_ACTIVELOCALE); + + public static final String CHANNEL_NAME_LOCALIZATIONCONFIGURATION_SUPPORTEDLOCALES = "SupportedLocales"; + public static final String CHANNEL_LABEL_LOCALIZATIONCONFIGURATION_SUPPORTEDLOCALES = "Supported Locales"; + public static final String CHANNEL_ID_LOCALIZATIONCONFIGURATION_SUPPORTEDLOCALES = "localizationconfiguration-supportedlocales"; + public static final ChannelTypeUID CHANNEL_LOCALIZATIONCONFIGURATION_SUPPORTEDLOCALES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_LOCALIZATIONCONFIGURATION_SUPPORTEDLOCALES); + + // LowPower Cluster + // MediaInput Cluster + public static final String CHANNEL_NAME_MEDIAINPUT_INPUTLIST = "InputList"; + public static final String CHANNEL_LABEL_MEDIAINPUT_INPUTLIST = "Input List"; + public static final String CHANNEL_ID_MEDIAINPUT_INPUTLIST = "mediainput-inputlist"; + public static final ChannelTypeUID CHANNEL_MEDIAINPUT_INPUTLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAINPUT_INPUTLIST); + + public static final String CHANNEL_NAME_MEDIAINPUT_CURRENTINPUT = "CurrentInput"; + public static final String CHANNEL_LABEL_MEDIAINPUT_CURRENTINPUT = "Current Input"; + public static final String CHANNEL_ID_MEDIAINPUT_CURRENTINPUT = "mediainput-currentinput"; + public static final ChannelTypeUID CHANNEL_MEDIAINPUT_CURRENTINPUT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAINPUT_CURRENTINPUT); + + // MediaPlayback Cluster + public static final String CHANNEL_NAME_MEDIAPLAYBACK_CURRENTSTATE = "CurrentState"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_CURRENTSTATE = "Current State"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_CURRENTSTATE = "mediaplayback-currentstate"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_CURRENTSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_CURRENTSTATE); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_STARTTIME = "StartTime"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_STARTTIME = "Start Time"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_STARTTIME = "mediaplayback-starttime"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_STARTTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_STARTTIME); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_DURATION = "Duration"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_DURATION = "Duration"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_DURATION = "mediaplayback-duration"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_DURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_DURATION); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_SAMPLEDPOSITION = "SampledPosition"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_SAMPLEDPOSITION = "Sampled Position"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_SAMPLEDPOSITION = "mediaplayback-sampledposition"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_SAMPLEDPOSITION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_SAMPLEDPOSITION); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_PLAYBACKSPEED = "PlaybackSpeed"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_PLAYBACKSPEED = "Playback Speed"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_PLAYBACKSPEED = "mediaplayback-playbackspeed"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_PLAYBACKSPEED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_PLAYBACKSPEED); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_SEEKRANGEEND = "SeekRangeEnd"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_SEEKRANGEEND = "Seek Range End"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_SEEKRANGEEND = "mediaplayback-seekrangeend"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_SEEKRANGEEND = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_SEEKRANGEEND); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_SEEKRANGESTART = "SeekRangeStart"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_SEEKRANGESTART = "Seek Range Start"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_SEEKRANGESTART = "mediaplayback-seekrangestart"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_SEEKRANGESTART = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_SEEKRANGESTART); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_ACTIVEAUDIOTRACK = "ActiveAudioTrack"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_ACTIVEAUDIOTRACK = "Active Audio Track"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_ACTIVEAUDIOTRACK = "mediaplayback-activeaudiotrack"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_ACTIVEAUDIOTRACK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_ACTIVEAUDIOTRACK); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_AVAILABLEAUDIOTRACKS = "AvailableAudioTracks"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_AVAILABLEAUDIOTRACKS = "Available Audio Tracks"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_AVAILABLEAUDIOTRACKS = "mediaplayback-availableaudiotracks"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_AVAILABLEAUDIOTRACKS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_AVAILABLEAUDIOTRACKS); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_ACTIVETEXTTRACK = "ActiveTextTrack"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_ACTIVETEXTTRACK = "Active Text Track"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_ACTIVETEXTTRACK = "mediaplayback-activetexttrack"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_ACTIVETEXTTRACK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_ACTIVETEXTTRACK); + + public static final String CHANNEL_NAME_MEDIAPLAYBACK_AVAILABLETEXTTRACKS = "AvailableTextTracks"; + public static final String CHANNEL_LABEL_MEDIAPLAYBACK_AVAILABLETEXTTRACKS = "Available Text Tracks"; + public static final String CHANNEL_ID_MEDIAPLAYBACK_AVAILABLETEXTTRACKS = "mediaplayback-availabletexttracks"; + public static final ChannelTypeUID CHANNEL_MEDIAPLAYBACK_AVAILABLETEXTTRACKS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MEDIAPLAYBACK_AVAILABLETEXTTRACKS); + + // MicrowaveOvenControl Cluster + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_COOKTIME = "CookTime"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_COOKTIME = "Cook Time"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_COOKTIME = "microwaveovencontrol-cooktime"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_COOKTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_COOKTIME); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_MAXCOOKTIME = "MaxCookTime"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_MAXCOOKTIME = "Max Cook Time"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_MAXCOOKTIME = "microwaveovencontrol-maxcooktime"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_MAXCOOKTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_MAXCOOKTIME); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_POWERSETTING = "PowerSetting"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_POWERSETTING = "Power Setting"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_POWERSETTING = "microwaveovencontrol-powersetting"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_POWERSETTING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_POWERSETTING); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_MINPOWER = "MinPower"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_MINPOWER = "Min Power"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_MINPOWER = "microwaveovencontrol-minpower"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_MINPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_MINPOWER); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_MAXPOWER = "MaxPower"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_MAXPOWER = "Max Power"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_MAXPOWER = "microwaveovencontrol-maxpower"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_MAXPOWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_MAXPOWER); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_POWERSTEP = "PowerStep"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_POWERSTEP = "Power Step"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_POWERSTEP = "microwaveovencontrol-powerstep"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_POWERSTEP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_POWERSTEP); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_SUPPORTEDWATTS = "SupportedWatts"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_SUPPORTEDWATTS = "Supported Watts"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_SUPPORTEDWATTS = "microwaveovencontrol-supportedwatts"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_SUPPORTEDWATTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_SUPPORTEDWATTS); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_SELECTEDWATTINDEX = "SelectedWattIndex"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_SELECTEDWATTINDEX = "Selected Watt Index"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_SELECTEDWATTINDEX = "microwaveovencontrol-selectedwattindex"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_SELECTEDWATTINDEX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_SELECTEDWATTINDEX); + + public static final String CHANNEL_NAME_MICROWAVEOVENCONTROL_WATTRATING = "WattRating"; + public static final String CHANNEL_LABEL_MICROWAVEOVENCONTROL_WATTRATING = "Watt Rating"; + public static final String CHANNEL_ID_MICROWAVEOVENCONTROL_WATTRATING = "microwaveovencontrol-wattrating"; + public static final ChannelTypeUID CHANNEL_MICROWAVEOVENCONTROL_WATTRATING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MICROWAVEOVENCONTROL_WATTRATING); + + // MicrowaveOvenMode Cluster + // ModeBase Cluster + public static final String CHANNEL_NAME_MODEBASE_SUPPORTEDMODES = "SupportedModes"; + public static final String CHANNEL_LABEL_MODEBASE_SUPPORTEDMODES = "Supported Modes"; + public static final String CHANNEL_ID_MODEBASE_SUPPORTEDMODES = "modebase-supportedmodes"; + public static final ChannelTypeUID CHANNEL_MODEBASE_SUPPORTEDMODES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODEBASE_SUPPORTEDMODES); + + public static final String CHANNEL_NAME_MODEBASE_CURRENTMODE = "CurrentMode"; + public static final String CHANNEL_LABEL_MODEBASE_CURRENTMODE = "Current Mode"; + public static final String CHANNEL_ID_MODEBASE_CURRENTMODE = "modebase-currentmode"; + public static final ChannelTypeUID CHANNEL_MODEBASE_CURRENTMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODEBASE_CURRENTMODE); + + public static final String CHANNEL_NAME_MODEBASE_STARTUPMODE = "StartUpMode"; + public static final String CHANNEL_LABEL_MODEBASE_STARTUPMODE = "Start Up Mode"; + public static final String CHANNEL_ID_MODEBASE_STARTUPMODE = "modebase-startupmode"; + public static final ChannelTypeUID CHANNEL_MODEBASE_STARTUPMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODEBASE_STARTUPMODE); + + public static final String CHANNEL_NAME_MODEBASE_ONMODE = "OnMode"; + public static final String CHANNEL_LABEL_MODEBASE_ONMODE = "On Mode"; + public static final String CHANNEL_ID_MODEBASE_ONMODE = "modebase-onmode"; + public static final ChannelTypeUID CHANNEL_MODEBASE_ONMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODEBASE_ONMODE); + + // ModeSelect Cluster + public static final String CHANNEL_NAME_MODESELECT_DESCRIPTION = "Description"; + public static final String CHANNEL_LABEL_MODESELECT_DESCRIPTION = "Description"; + public static final String CHANNEL_ID_MODESELECT_DESCRIPTION = "modeselect-description"; + public static final ChannelTypeUID CHANNEL_MODESELECT_DESCRIPTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODESELECT_DESCRIPTION); + + public static final String CHANNEL_NAME_MODESELECT_STANDARDNAMESPACE = "StandardNamespace"; + public static final String CHANNEL_LABEL_MODESELECT_STANDARDNAMESPACE = "Standard Namespace"; + public static final String CHANNEL_ID_MODESELECT_STANDARDNAMESPACE = "modeselect-standardnamespace"; + public static final ChannelTypeUID CHANNEL_MODESELECT_STANDARDNAMESPACE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODESELECT_STANDARDNAMESPACE); + + public static final String CHANNEL_NAME_MODESELECT_SUPPORTEDMODES = "SupportedModes"; + public static final String CHANNEL_LABEL_MODESELECT_SUPPORTEDMODES = "Supported Modes"; + public static final String CHANNEL_ID_MODESELECT_SUPPORTEDMODES = "modeselect-supportedmodes"; + public static final ChannelTypeUID CHANNEL_MODESELECT_SUPPORTEDMODES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODESELECT_SUPPORTEDMODES); + + public static final String CHANNEL_NAME_MODESELECT_CURRENTMODE = "CurrentMode"; + public static final String CHANNEL_LABEL_MODESELECT_CURRENTMODE = "Current Mode"; + public static final String CHANNEL_ID_MODESELECT_CURRENTMODE = "modeselect-currentmode"; + public static final ChannelTypeUID CHANNEL_MODESELECT_CURRENTMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODESELECT_CURRENTMODE); + + public static final String CHANNEL_NAME_MODESELECT_STARTUPMODE = "StartUpMode"; + public static final String CHANNEL_LABEL_MODESELECT_STARTUPMODE = "Start Up Mode"; + public static final String CHANNEL_ID_MODESELECT_STARTUPMODE = "modeselect-startupmode"; + public static final ChannelTypeUID CHANNEL_MODESELECT_STARTUPMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODESELECT_STARTUPMODE); + + public static final String CHANNEL_NAME_MODESELECT_ONMODE = "OnMode"; + public static final String CHANNEL_LABEL_MODESELECT_ONMODE = "On Mode"; + public static final String CHANNEL_ID_MODESELECT_ONMODE = "modeselect-onmode"; + public static final ChannelTypeUID CHANNEL_MODESELECT_ONMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_MODESELECT_ONMODE); + + // NetworkCommissioning Cluster + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_MAXNETWORKS = "MaxNetworks"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_MAXNETWORKS = "Max Networks"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_MAXNETWORKS = "networkcommissioning-maxnetworks"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_MAXNETWORKS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_MAXNETWORKS); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_NETWORKS = "Networks"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_NETWORKS = "Networks"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_NETWORKS = "networkcommissioning-networks"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_NETWORKS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_NETWORKS); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_SCANMAXTIMESECONDS = "ScanMaxTimeSeconds"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_SCANMAXTIMESECONDS = "Scan Max Time Seconds"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_SCANMAXTIMESECONDS = "networkcommissioning-scanmaxtimeseconds"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_SCANMAXTIMESECONDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_SCANMAXTIMESECONDS); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_CONNECTMAXTIMESECONDS = "ConnectMaxTimeSeconds"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_CONNECTMAXTIMESECONDS = "Connect Max Time Seconds"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_CONNECTMAXTIMESECONDS = "networkcommissioning-connectmaxtimeseconds"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_CONNECTMAXTIMESECONDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_CONNECTMAXTIMESECONDS); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_INTERFACEENABLED = "InterfaceEnabled"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_INTERFACEENABLED = "Interface Enabled"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_INTERFACEENABLED = "networkcommissioning-interfaceenabled"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_INTERFACEENABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_INTERFACEENABLED); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_LASTNETWORKINGSTATUS = "LastNetworkingStatus"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_LASTNETWORKINGSTATUS = "Last Networking Status"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_LASTNETWORKINGSTATUS = "networkcommissioning-lastnetworkingstatus"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_LASTNETWORKINGSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_LASTNETWORKINGSTATUS); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_LASTNETWORKID = "LastNetworkId"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_LASTNETWORKID = "Last Network Id"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_LASTNETWORKID = "networkcommissioning-lastnetworkid"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_LASTNETWORKID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_LASTNETWORKID); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_LASTCONNECTERRORVALUE = "LastConnectErrorValue"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_LASTCONNECTERRORVALUE = "Last Connect Error Value"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_LASTCONNECTERRORVALUE = "networkcommissioning-lastconnecterrorvalue"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_LASTCONNECTERRORVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_LASTCONNECTERRORVALUE); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_SUPPORTEDWIFIBANDS = "SupportedWiFiBands"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_SUPPORTEDWIFIBANDS = "Supported Wi Fi Bands"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_SUPPORTEDWIFIBANDS = "networkcommissioning-supportedwifibands"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_SUPPORTEDWIFIBANDS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_SUPPORTEDWIFIBANDS); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_SUPPORTEDTHREADFEATURES = "SupportedThreadFeatures"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_SUPPORTEDTHREADFEATURES = "Supported Thread Features"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_SUPPORTEDTHREADFEATURES = "networkcommissioning-supportedthreadfeatures"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_SUPPORTEDTHREADFEATURES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_SUPPORTEDTHREADFEATURES); + + public static final String CHANNEL_NAME_NETWORKCOMMISSIONING_THREADVERSION = "ThreadVersion"; + public static final String CHANNEL_LABEL_NETWORKCOMMISSIONING_THREADVERSION = "Thread Version"; + public static final String CHANNEL_ID_NETWORKCOMMISSIONING_THREADVERSION = "networkcommissioning-threadversion"; + public static final ChannelTypeUID CHANNEL_NETWORKCOMMISSIONING_THREADVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_NETWORKCOMMISSIONING_THREADVERSION); + + // NitrogenDioxideConcentrationMeasurement Cluster + // OccupancySensing Cluster + public static final String CHANNEL_NAME_OCCUPANCYSENSING_OCCUPANCY = "Occupancy"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_OCCUPANCY = "Occupancy"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_OCCUPANCY = "occupancysensing-occupancy"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_OCCUPANCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_OCCUPANCY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_OCCUPANCYSENSORTYPE = "OccupancySensorType"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_OCCUPANCYSENSORTYPE = "Occupancy Sensor Type"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_OCCUPANCYSENSORTYPE = "occupancysensing-occupancysensortype"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_OCCUPANCYSENSORTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_OCCUPANCYSENSORTYPE); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_OCCUPANCYSENSORTYPEBITMAP = "OccupancySensorTypeBitmap"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_OCCUPANCYSENSORTYPEBITMAP = "Occupancy Sensor Type Bitmap"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_OCCUPANCYSENSORTYPEBITMAP = "occupancysensing-occupancysensortypebitmap"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_OCCUPANCYSENSORTYPEBITMAP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_OCCUPANCYSENSORTYPEBITMAP); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_HOLDTIME = "HoldTime"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_HOLDTIME = "Hold Time"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_HOLDTIME = "occupancysensing-holdtime"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_HOLDTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_HOLDTIME); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_HOLDTIMELIMITS = "HoldTimeLimits"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_HOLDTIMELIMITS = "Hold Time Limits"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_HOLDTIMELIMITS = "occupancysensing-holdtimelimits"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_HOLDTIMELIMITS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_HOLDTIMELIMITS); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_PIROCCUPIEDTOUNOCCUPIEDDELAY = "PirOccupiedToUnoccupiedDelay"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_PIROCCUPIEDTOUNOCCUPIEDDELAY = "Pir Occupied To Unoccupied Delay"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_PIROCCUPIEDTOUNOCCUPIEDDELAY = "occupancysensing-piroccupiedtounoccupieddelay"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_PIROCCUPIEDTOUNOCCUPIEDDELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_PIROCCUPIEDTOUNOCCUPIEDDELAY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDDELAY = "PirUnoccupiedToOccupiedDelay"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDDELAY = "Pir Unoccupied To Occupied Delay"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDDELAY = "occupancysensing-pirunoccupiedtooccupieddelay"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDDELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDDELAY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "PirUnoccupiedToOccupiedThreshold"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "Pir Unoccupied To Occupied Threshold"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "occupancysensing-pirunoccupiedtooccupiedthreshold"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDTHRESHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_PIRUNOCCUPIEDTOOCCUPIEDTHRESHOLD); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_ULTRASONICOCCUPIEDTOUNOCCUPIEDDELAY = "UltrasonicOccupiedToUnoccupiedDelay"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_ULTRASONICOCCUPIEDTOUNOCCUPIEDDELAY = "Ultrasonic Occupied To Unoccupied Delay"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_ULTRASONICOCCUPIEDTOUNOCCUPIEDDELAY = "occupancysensing-ultrasonicoccupiedtounoccupieddelay"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_ULTRASONICOCCUPIEDTOUNOCCUPIEDDELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_ULTRASONICOCCUPIEDTOUNOCCUPIEDDELAY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDDELAY = "UltrasonicUnoccupiedToOccupiedDelay"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDDELAY = "Ultrasonic Unoccupied To Occupied Delay"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDDELAY = "occupancysensing-ultrasonicunoccupiedtooccupieddelay"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDDELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDDELAY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "UltrasonicUnoccupiedToOccupiedThreshold"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "Ultrasonic Unoccupied To Occupied Threshold"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "occupancysensing-ultrasonicunoccupiedtooccupiedthreshold"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDTHRESHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_ULTRASONICUNOCCUPIEDTOOCCUPIEDTHRESHOLD); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_PHYSICALCONTACTOCCUPIEDTOUNOCCUPIEDDELAY = "PhysicalContactOccupiedToUnoccupiedDelay"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_PHYSICALCONTACTOCCUPIEDTOUNOCCUPIEDDELAY = "Physical Contact Occupied To Unoccupied Delay"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_PHYSICALCONTACTOCCUPIEDTOUNOCCUPIEDDELAY = "occupancysensing-physicalcontactoccupiedtounoccupieddelay"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_PHYSICALCONTACTOCCUPIEDTOUNOCCUPIEDDELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_PHYSICALCONTACTOCCUPIEDTOUNOCCUPIEDDELAY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDDELAY = "PhysicalContactUnoccupiedToOccupiedDelay"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDDELAY = "Physical Contact Unoccupied To Occupied Delay"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDDELAY = "occupancysensing-physicalcontactunoccupiedtooccupieddelay"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDDELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDDELAY); + + public static final String CHANNEL_NAME_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "PhysicalContactUnoccupiedToOccupiedThreshold"; + public static final String CHANNEL_LABEL_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "Physical Contact Unoccupied To Occupied Threshold"; + public static final String CHANNEL_ID_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDTHRESHOLD = "occupancysensing-physicalcontactunoccupiedtooccupiedthreshold"; + public static final ChannelTypeUID CHANNEL_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDTHRESHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OCCUPANCYSENSING_PHYSICALCONTACTUNOCCUPIEDTOOCCUPIEDTHRESHOLD); + + // OnOff Cluster + public static final String CHANNEL_NAME_ONOFF_ONOFF = "OnOff"; + public static final String CHANNEL_LABEL_ONOFF_ONOFF = "On Off"; + public static final String CHANNEL_ID_ONOFF_ONOFF = "onoff-onoff"; + public static final ChannelTypeUID CHANNEL_ONOFF_ONOFF = new ChannelTypeUID("matter:" + CHANNEL_ID_ONOFF_ONOFF); + + public static final String CHANNEL_NAME_ONOFF_GLOBALSCENECONTROL = "GlobalSceneControl"; + public static final String CHANNEL_LABEL_ONOFF_GLOBALSCENECONTROL = "Global Scene Control"; + public static final String CHANNEL_ID_ONOFF_GLOBALSCENECONTROL = "onoff-globalscenecontrol"; + public static final ChannelTypeUID CHANNEL_ONOFF_GLOBALSCENECONTROL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ONOFF_GLOBALSCENECONTROL); + + public static final String CHANNEL_NAME_ONOFF_ONTIME = "OnTime"; + public static final String CHANNEL_LABEL_ONOFF_ONTIME = "On Time"; + public static final String CHANNEL_ID_ONOFF_ONTIME = "onoff-ontime"; + public static final ChannelTypeUID CHANNEL_ONOFF_ONTIME = new ChannelTypeUID("matter:" + CHANNEL_ID_ONOFF_ONTIME); + + public static final String CHANNEL_NAME_ONOFF_OFFWAITTIME = "OffWaitTime"; + public static final String CHANNEL_LABEL_ONOFF_OFFWAITTIME = "Off Wait Time"; + public static final String CHANNEL_ID_ONOFF_OFFWAITTIME = "onoff-offwaittime"; + public static final ChannelTypeUID CHANNEL_ONOFF_OFFWAITTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ONOFF_OFFWAITTIME); + + public static final String CHANNEL_NAME_ONOFF_STARTUPONOFF = "StartUpOnOff"; + public static final String CHANNEL_LABEL_ONOFF_STARTUPONOFF = "Start Up On Off"; + public static final String CHANNEL_ID_ONOFF_STARTUPONOFF = "onoff-startuponoff"; + public static final ChannelTypeUID CHANNEL_ONOFF_STARTUPONOFF = new ChannelTypeUID( + "matter:" + CHANNEL_ID_ONOFF_STARTUPONOFF); + + // OperationalCredentials Cluster + public static final String CHANNEL_NAME_OPERATIONALCREDENTIALS_NOCS = "Nocs"; + public static final String CHANNEL_LABEL_OPERATIONALCREDENTIALS_NOCS = "Nocs"; + public static final String CHANNEL_ID_OPERATIONALCREDENTIALS_NOCS = "operationalcredentials-nocs"; + public static final ChannelTypeUID CHANNEL_OPERATIONALCREDENTIALS_NOCS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALCREDENTIALS_NOCS); + + public static final String CHANNEL_NAME_OPERATIONALCREDENTIALS_FABRICS = "Fabrics"; + public static final String CHANNEL_LABEL_OPERATIONALCREDENTIALS_FABRICS = "Fabrics"; + public static final String CHANNEL_ID_OPERATIONALCREDENTIALS_FABRICS = "operationalcredentials-fabrics"; + public static final ChannelTypeUID CHANNEL_OPERATIONALCREDENTIALS_FABRICS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALCREDENTIALS_FABRICS); + + public static final String CHANNEL_NAME_OPERATIONALCREDENTIALS_SUPPORTEDFABRICS = "SupportedFabrics"; + public static final String CHANNEL_LABEL_OPERATIONALCREDENTIALS_SUPPORTEDFABRICS = "Supported Fabrics"; + public static final String CHANNEL_ID_OPERATIONALCREDENTIALS_SUPPORTEDFABRICS = "operationalcredentials-supportedfabrics"; + public static final ChannelTypeUID CHANNEL_OPERATIONALCREDENTIALS_SUPPORTEDFABRICS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALCREDENTIALS_SUPPORTEDFABRICS); + + public static final String CHANNEL_NAME_OPERATIONALCREDENTIALS_COMMISSIONEDFABRICS = "CommissionedFabrics"; + public static final String CHANNEL_LABEL_OPERATIONALCREDENTIALS_COMMISSIONEDFABRICS = "Commissioned Fabrics"; + public static final String CHANNEL_ID_OPERATIONALCREDENTIALS_COMMISSIONEDFABRICS = "operationalcredentials-commissionedfabrics"; + public static final ChannelTypeUID CHANNEL_OPERATIONALCREDENTIALS_COMMISSIONEDFABRICS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALCREDENTIALS_COMMISSIONEDFABRICS); + + public static final String CHANNEL_NAME_OPERATIONALCREDENTIALS_TRUSTEDROOTCERTIFICATES = "TrustedRootCertificates"; + public static final String CHANNEL_LABEL_OPERATIONALCREDENTIALS_TRUSTEDROOTCERTIFICATES = "Trusted Root Certificates"; + public static final String CHANNEL_ID_OPERATIONALCREDENTIALS_TRUSTEDROOTCERTIFICATES = "operationalcredentials-trustedrootcertificates"; + public static final ChannelTypeUID CHANNEL_OPERATIONALCREDENTIALS_TRUSTEDROOTCERTIFICATES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALCREDENTIALS_TRUSTEDROOTCERTIFICATES); + + public static final String CHANNEL_NAME_OPERATIONALCREDENTIALS_CURRENTFABRICINDEX = "CurrentFabricIndex"; + public static final String CHANNEL_LABEL_OPERATIONALCREDENTIALS_CURRENTFABRICINDEX = "Current Fabric Index"; + public static final String CHANNEL_ID_OPERATIONALCREDENTIALS_CURRENTFABRICINDEX = "operationalcredentials-currentfabricindex"; + public static final ChannelTypeUID CHANNEL_OPERATIONALCREDENTIALS_CURRENTFABRICINDEX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALCREDENTIALS_CURRENTFABRICINDEX); + + // OperationalState Cluster + public static final String CHANNEL_NAME_OPERATIONALSTATE_PHASELIST = "PhaseList"; + public static final String CHANNEL_LABEL_OPERATIONALSTATE_PHASELIST = "Phase List"; + public static final String CHANNEL_ID_OPERATIONALSTATE_PHASELIST = "operationalstate-phaselist"; + public static final ChannelTypeUID CHANNEL_OPERATIONALSTATE_PHASELIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALSTATE_PHASELIST); + + public static final String CHANNEL_NAME_OPERATIONALSTATE_CURRENTPHASE = "CurrentPhase"; + public static final String CHANNEL_LABEL_OPERATIONALSTATE_CURRENTPHASE = "Current Phase"; + public static final String CHANNEL_ID_OPERATIONALSTATE_CURRENTPHASE = "operationalstate-currentphase"; + public static final ChannelTypeUID CHANNEL_OPERATIONALSTATE_CURRENTPHASE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALSTATE_CURRENTPHASE); + + public static final String CHANNEL_NAME_OPERATIONALSTATE_COUNTDOWNTIME = "CountdownTime"; + public static final String CHANNEL_LABEL_OPERATIONALSTATE_COUNTDOWNTIME = "Countdown Time"; + public static final String CHANNEL_ID_OPERATIONALSTATE_COUNTDOWNTIME = "operationalstate-countdowntime"; + public static final ChannelTypeUID CHANNEL_OPERATIONALSTATE_COUNTDOWNTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALSTATE_COUNTDOWNTIME); + + public static final String CHANNEL_NAME_OPERATIONALSTATE_OPERATIONALSTATELIST = "OperationalStateList"; + public static final String CHANNEL_LABEL_OPERATIONALSTATE_OPERATIONALSTATELIST = "Operational State List"; + public static final String CHANNEL_ID_OPERATIONALSTATE_OPERATIONALSTATELIST = "operationalstate-operationalstatelist"; + public static final ChannelTypeUID CHANNEL_OPERATIONALSTATE_OPERATIONALSTATELIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALSTATE_OPERATIONALSTATELIST); + + public static final String CHANNEL_NAME_OPERATIONALSTATE_OPERATIONALSTATE = "OperationalState"; + public static final String CHANNEL_LABEL_OPERATIONALSTATE_OPERATIONALSTATE = "Operational State"; + public static final String CHANNEL_ID_OPERATIONALSTATE_OPERATIONALSTATE = "operationalstate-operationalstate"; + public static final ChannelTypeUID CHANNEL_OPERATIONALSTATE_OPERATIONALSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALSTATE_OPERATIONALSTATE); + + public static final String CHANNEL_NAME_OPERATIONALSTATE_OPERATIONALERROR = "OperationalError"; + public static final String CHANNEL_LABEL_OPERATIONALSTATE_OPERATIONALERROR = "Operational Error"; + public static final String CHANNEL_ID_OPERATIONALSTATE_OPERATIONALERROR = "operationalstate-operationalerror"; + public static final ChannelTypeUID CHANNEL_OPERATIONALSTATE_OPERATIONALERROR = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OPERATIONALSTATE_OPERATIONALERROR); + + // OtaSoftwareUpdateProvider Cluster + // OtaSoftwareUpdateRequestor Cluster + public static final String CHANNEL_NAME_OTASOFTWAREUPDATEREQUESTOR_DEFAULTOTAPROVIDERS = "DefaultOtaProviders"; + public static final String CHANNEL_LABEL_OTASOFTWAREUPDATEREQUESTOR_DEFAULTOTAPROVIDERS = "Default Ota Providers"; + public static final String CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_DEFAULTOTAPROVIDERS = "otasoftwareupdaterequestor-defaultotaproviders"; + public static final ChannelTypeUID CHANNEL_OTASOFTWAREUPDATEREQUESTOR_DEFAULTOTAPROVIDERS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_DEFAULTOTAPROVIDERS); + + public static final String CHANNEL_NAME_OTASOFTWAREUPDATEREQUESTOR_UPDATEPOSSIBLE = "UpdatePossible"; + public static final String CHANNEL_LABEL_OTASOFTWAREUPDATEREQUESTOR_UPDATEPOSSIBLE = "Update Possible"; + public static final String CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATEPOSSIBLE = "otasoftwareupdaterequestor-updatepossible"; + public static final ChannelTypeUID CHANNEL_OTASOFTWAREUPDATEREQUESTOR_UPDATEPOSSIBLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATEPOSSIBLE); + + public static final String CHANNEL_NAME_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATE = "UpdateState"; + public static final String CHANNEL_LABEL_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATE = "Update State"; + public static final String CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATE = "otasoftwareupdaterequestor-updatestate"; + public static final ChannelTypeUID CHANNEL_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATE); + + public static final String CHANNEL_NAME_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATEPROGRESS = "UpdateStateProgress"; + public static final String CHANNEL_LABEL_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATEPROGRESS = "Update State Progress"; + public static final String CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATEPROGRESS = "otasoftwareupdaterequestor-updatestateprogress"; + public static final ChannelTypeUID CHANNEL_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATEPROGRESS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_OTASOFTWAREUPDATEREQUESTOR_UPDATESTATEPROGRESS); + + // OvenCavityOperationalState Cluster + // OvenMode Cluster + // OzoneConcentrationMeasurement Cluster + // Pm10ConcentrationMeasurement Cluster + // Pm1ConcentrationMeasurement Cluster + // Pm25ConcentrationMeasurement Cluster + // PowerSource Cluster + public static final String CHANNEL_NAME_POWERSOURCE_STATUS = "Status"; + public static final String CHANNEL_LABEL_POWERSOURCE_STATUS = "Status"; + public static final String CHANNEL_ID_POWERSOURCE_STATUS = "powersource-status"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_STATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_STATUS); + + public static final String CHANNEL_NAME_POWERSOURCE_ORDER = "Order"; + public static final String CHANNEL_LABEL_POWERSOURCE_ORDER = "Order"; + public static final String CHANNEL_ID_POWERSOURCE_ORDER = "powersource-order"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_ORDER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_ORDER); + + public static final String CHANNEL_NAME_POWERSOURCE_DESCRIPTION = "Description"; + public static final String CHANNEL_LABEL_POWERSOURCE_DESCRIPTION = "Description"; + public static final String CHANNEL_ID_POWERSOURCE_DESCRIPTION = "powersource-description"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_DESCRIPTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_DESCRIPTION); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDASSESSEDINPUTVOLTAGE = "WiredAssessedInputVoltage"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDASSESSEDINPUTVOLTAGE = "Wired Assessed Input Voltage"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDASSESSEDINPUTVOLTAGE = "powersource-wiredassessedinputvoltage"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDASSESSEDINPUTVOLTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDASSESSEDINPUTVOLTAGE); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDASSESSEDINPUTFREQUENCY = "WiredAssessedInputFrequency"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDASSESSEDINPUTFREQUENCY = "Wired Assessed Input Frequency"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDASSESSEDINPUTFREQUENCY = "powersource-wiredassessedinputfrequency"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDASSESSEDINPUTFREQUENCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDASSESSEDINPUTFREQUENCY); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDCURRENTTYPE = "WiredCurrentType"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDCURRENTTYPE = "Wired Current Type"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDCURRENTTYPE = "powersource-wiredcurrenttype"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDCURRENTTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDCURRENTTYPE); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDASSESSEDCURRENT = "WiredAssessedCurrent"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDASSESSEDCURRENT = "Wired Assessed Current"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDASSESSEDCURRENT = "powersource-wiredassessedcurrent"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDASSESSEDCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDASSESSEDCURRENT); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDNOMINALVOLTAGE = "WiredNominalVoltage"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDNOMINALVOLTAGE = "Wired Nominal Voltage"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDNOMINALVOLTAGE = "powersource-wirednominalvoltage"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDNOMINALVOLTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDNOMINALVOLTAGE); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDMAXIMUMCURRENT = "WiredMaximumCurrent"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDMAXIMUMCURRENT = "Wired Maximum Current"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDMAXIMUMCURRENT = "powersource-wiredmaximumcurrent"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDMAXIMUMCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDMAXIMUMCURRENT); + + public static final String CHANNEL_NAME_POWERSOURCE_WIREDPRESENT = "WiredPresent"; + public static final String CHANNEL_LABEL_POWERSOURCE_WIREDPRESENT = "Wired Present"; + public static final String CHANNEL_ID_POWERSOURCE_WIREDPRESENT = "powersource-wiredpresent"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_WIREDPRESENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_WIREDPRESENT); + + public static final String CHANNEL_NAME_POWERSOURCE_ACTIVEWIREDFAULTS = "ActiveWiredFaults"; + public static final String CHANNEL_LABEL_POWERSOURCE_ACTIVEWIREDFAULTS = "Active Wired Faults"; + public static final String CHANNEL_ID_POWERSOURCE_ACTIVEWIREDFAULTS = "powersource-activewiredfaults"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_ACTIVEWIREDFAULTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_ACTIVEWIREDFAULTS); + + public static final String CHANNEL_NAME_POWERSOURCE_BATVOLTAGE = "BatVoltage"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATVOLTAGE = "Bat Voltage"; + public static final String CHANNEL_ID_POWERSOURCE_BATVOLTAGE = "powersource-batvoltage"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATVOLTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATVOLTAGE); + + public static final String CHANNEL_NAME_POWERSOURCE_BATPERCENTREMAINING = "BatPercentRemaining"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATPERCENTREMAINING = "Bat Percent Remaining"; + public static final String CHANNEL_ID_POWERSOURCE_BATPERCENTREMAINING = "powersource-batpercentremaining"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATPERCENTREMAINING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATPERCENTREMAINING); + + public static final String CHANNEL_NAME_POWERSOURCE_BATTIMEREMAINING = "BatTimeRemaining"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATTIMEREMAINING = "Bat Time Remaining"; + public static final String CHANNEL_ID_POWERSOURCE_BATTIMEREMAINING = "powersource-battimeremaining"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATTIMEREMAINING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATTIMEREMAINING); + + public static final String CHANNEL_NAME_POWERSOURCE_BATCHARGELEVEL = "BatChargeLevel"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATCHARGELEVEL = "Bat Charge Level"; + public static final String CHANNEL_ID_POWERSOURCE_BATCHARGELEVEL = "powersource-batchargelevel"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATCHARGELEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATCHARGELEVEL); + + public static final String CHANNEL_NAME_POWERSOURCE_BATREPLACEMENTNEEDED = "BatReplacementNeeded"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATREPLACEMENTNEEDED = "Bat Replacement Needed"; + public static final String CHANNEL_ID_POWERSOURCE_BATREPLACEMENTNEEDED = "powersource-batreplacementneeded"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATREPLACEMENTNEEDED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATREPLACEMENTNEEDED); + + public static final String CHANNEL_NAME_POWERSOURCE_BATREPLACEABILITY = "BatReplaceability"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATREPLACEABILITY = "Bat Replaceability"; + public static final String CHANNEL_ID_POWERSOURCE_BATREPLACEABILITY = "powersource-batreplaceability"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATREPLACEABILITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATREPLACEABILITY); + + public static final String CHANNEL_NAME_POWERSOURCE_BATPRESENT = "BatPresent"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATPRESENT = "Bat Present"; + public static final String CHANNEL_ID_POWERSOURCE_BATPRESENT = "powersource-batpresent"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATPRESENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATPRESENT); + + public static final String CHANNEL_NAME_POWERSOURCE_ACTIVEBATFAULTS = "ActiveBatFaults"; + public static final String CHANNEL_LABEL_POWERSOURCE_ACTIVEBATFAULTS = "Active Bat Faults"; + public static final String CHANNEL_ID_POWERSOURCE_ACTIVEBATFAULTS = "powersource-activebatfaults"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_ACTIVEBATFAULTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_ACTIVEBATFAULTS); + + public static final String CHANNEL_NAME_POWERSOURCE_BATREPLACEMENTDESCRIPTION = "BatReplacementDescription"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATREPLACEMENTDESCRIPTION = "Bat Replacement Description"; + public static final String CHANNEL_ID_POWERSOURCE_BATREPLACEMENTDESCRIPTION = "powersource-batreplacementdescription"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATREPLACEMENTDESCRIPTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATREPLACEMENTDESCRIPTION); + + public static final String CHANNEL_NAME_POWERSOURCE_BATCOMMONDESIGNATION = "BatCommonDesignation"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATCOMMONDESIGNATION = "Bat Common Designation"; + public static final String CHANNEL_ID_POWERSOURCE_BATCOMMONDESIGNATION = "powersource-batcommondesignation"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATCOMMONDESIGNATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATCOMMONDESIGNATION); + + public static final String CHANNEL_NAME_POWERSOURCE_BATANSIDESIGNATION = "BatAnsiDesignation"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATANSIDESIGNATION = "Bat Ansi Designation"; + public static final String CHANNEL_ID_POWERSOURCE_BATANSIDESIGNATION = "powersource-batansidesignation"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATANSIDESIGNATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATANSIDESIGNATION); + + public static final String CHANNEL_NAME_POWERSOURCE_BATIECDESIGNATION = "BatIecDesignation"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATIECDESIGNATION = "Bat Iec Designation"; + public static final String CHANNEL_ID_POWERSOURCE_BATIECDESIGNATION = "powersource-batiecdesignation"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATIECDESIGNATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATIECDESIGNATION); + + public static final String CHANNEL_NAME_POWERSOURCE_BATAPPROVEDCHEMISTRY = "BatApprovedChemistry"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATAPPROVEDCHEMISTRY = "Bat Approved Chemistry"; + public static final String CHANNEL_ID_POWERSOURCE_BATAPPROVEDCHEMISTRY = "powersource-batapprovedchemistry"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATAPPROVEDCHEMISTRY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATAPPROVEDCHEMISTRY); + + public static final String CHANNEL_NAME_POWERSOURCE_BATCAPACITY = "BatCapacity"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATCAPACITY = "Bat Capacity"; + public static final String CHANNEL_ID_POWERSOURCE_BATCAPACITY = "powersource-batcapacity"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATCAPACITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATCAPACITY); + + public static final String CHANNEL_NAME_POWERSOURCE_BATQUANTITY = "BatQuantity"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATQUANTITY = "Bat Quantity"; + public static final String CHANNEL_ID_POWERSOURCE_BATQUANTITY = "powersource-batquantity"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATQUANTITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATQUANTITY); + + public static final String CHANNEL_NAME_POWERSOURCE_BATCHARGESTATE = "BatChargeState"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATCHARGESTATE = "Bat Charge State"; + public static final String CHANNEL_ID_POWERSOURCE_BATCHARGESTATE = "powersource-batchargestate"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATCHARGESTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATCHARGESTATE); + + public static final String CHANNEL_NAME_POWERSOURCE_BATTIMETOFULLCHARGE = "BatTimeToFullCharge"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATTIMETOFULLCHARGE = "Bat Time To Full Charge"; + public static final String CHANNEL_ID_POWERSOURCE_BATTIMETOFULLCHARGE = "powersource-battimetofullcharge"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATTIMETOFULLCHARGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATTIMETOFULLCHARGE); + + public static final String CHANNEL_NAME_POWERSOURCE_BATFUNCTIONALWHILECHARGING = "BatFunctionalWhileCharging"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATFUNCTIONALWHILECHARGING = "Bat Functional While Charging"; + public static final String CHANNEL_ID_POWERSOURCE_BATFUNCTIONALWHILECHARGING = "powersource-batfunctionalwhilecharging"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATFUNCTIONALWHILECHARGING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATFUNCTIONALWHILECHARGING); + + public static final String CHANNEL_NAME_POWERSOURCE_BATCHARGINGCURRENT = "BatChargingCurrent"; + public static final String CHANNEL_LABEL_POWERSOURCE_BATCHARGINGCURRENT = "Bat Charging Current"; + public static final String CHANNEL_ID_POWERSOURCE_BATCHARGINGCURRENT = "powersource-batchargingcurrent"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_BATCHARGINGCURRENT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_BATCHARGINGCURRENT); + + public static final String CHANNEL_NAME_POWERSOURCE_ACTIVEBATCHARGEFAULTS = "ActiveBatChargeFaults"; + public static final String CHANNEL_LABEL_POWERSOURCE_ACTIVEBATCHARGEFAULTS = "Active Bat Charge Faults"; + public static final String CHANNEL_ID_POWERSOURCE_ACTIVEBATCHARGEFAULTS = "powersource-activebatchargefaults"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_ACTIVEBATCHARGEFAULTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_ACTIVEBATCHARGEFAULTS); + + public static final String CHANNEL_NAME_POWERSOURCE_ENDPOINTLIST = "EndpointList"; + public static final String CHANNEL_LABEL_POWERSOURCE_ENDPOINTLIST = "Endpoint List"; + public static final String CHANNEL_ID_POWERSOURCE_ENDPOINTLIST = "powersource-endpointlist"; + public static final ChannelTypeUID CHANNEL_POWERSOURCE_ENDPOINTLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCE_ENDPOINTLIST); + + // PowerSourceConfiguration Cluster + public static final String CHANNEL_NAME_POWERSOURCECONFIGURATION_SOURCES = "Sources"; + public static final String CHANNEL_LABEL_POWERSOURCECONFIGURATION_SOURCES = "Sources"; + public static final String CHANNEL_ID_POWERSOURCECONFIGURATION_SOURCES = "powersourceconfiguration-sources"; + public static final ChannelTypeUID CHANNEL_POWERSOURCECONFIGURATION_SOURCES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERSOURCECONFIGURATION_SOURCES); + + // PowerTopology Cluster + public static final String CHANNEL_NAME_POWERTOPOLOGY_AVAILABLEENDPOINTS = "AvailableEndpoints"; + public static final String CHANNEL_LABEL_POWERTOPOLOGY_AVAILABLEENDPOINTS = "Available Endpoints"; + public static final String CHANNEL_ID_POWERTOPOLOGY_AVAILABLEENDPOINTS = "powertopology-availableendpoints"; + public static final ChannelTypeUID CHANNEL_POWERTOPOLOGY_AVAILABLEENDPOINTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERTOPOLOGY_AVAILABLEENDPOINTS); + + public static final String CHANNEL_NAME_POWERTOPOLOGY_ACTIVEENDPOINTS = "ActiveEndpoints"; + public static final String CHANNEL_LABEL_POWERTOPOLOGY_ACTIVEENDPOINTS = "Active Endpoints"; + public static final String CHANNEL_ID_POWERTOPOLOGY_ACTIVEENDPOINTS = "powertopology-activeendpoints"; + public static final ChannelTypeUID CHANNEL_POWERTOPOLOGY_ACTIVEENDPOINTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_POWERTOPOLOGY_ACTIVEENDPOINTS); + + // PressureMeasurement Cluster + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_MEASUREDVALUE = "MeasuredValue"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_MEASUREDVALUE = "Measured Value"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_MEASUREDVALUE = "pressuremeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_MEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_MEASUREDVALUE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_MINMEASUREDVALUE = "MinMeasuredValue"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_MINMEASUREDVALUE = "Min Measured Value"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_MINMEASUREDVALUE = "pressuremeasurement-minmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_MINMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_MINMEASUREDVALUE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_MAXMEASUREDVALUE = "MaxMeasuredValue"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_MAXMEASUREDVALUE = "Max Measured Value"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_MAXMEASUREDVALUE = "pressuremeasurement-maxmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_MAXMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_MAXMEASUREDVALUE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_TOLERANCE = "pressuremeasurement-tolerance"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_TOLERANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_TOLERANCE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_SCALEDVALUE = "ScaledValue"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_SCALEDVALUE = "Scaled Value"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_SCALEDVALUE = "pressuremeasurement-scaledvalue"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_SCALEDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_SCALEDVALUE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_MINSCALEDVALUE = "MinScaledValue"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_MINSCALEDVALUE = "Min Scaled Value"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_MINSCALEDVALUE = "pressuremeasurement-minscaledvalue"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_MINSCALEDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_MINSCALEDVALUE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_MAXSCALEDVALUE = "MaxScaledValue"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_MAXSCALEDVALUE = "Max Scaled Value"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_MAXSCALEDVALUE = "pressuremeasurement-maxscaledvalue"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_MAXSCALEDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_MAXSCALEDVALUE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_SCALEDTOLERANCE = "ScaledTolerance"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_SCALEDTOLERANCE = "Scaled Tolerance"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_SCALEDTOLERANCE = "pressuremeasurement-scaledtolerance"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_SCALEDTOLERANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_SCALEDTOLERANCE); + + public static final String CHANNEL_NAME_PRESSUREMEASUREMENT_SCALE = "Scale"; + public static final String CHANNEL_LABEL_PRESSUREMEASUREMENT_SCALE = "Scale"; + public static final String CHANNEL_ID_PRESSUREMEASUREMENT_SCALE = "pressuremeasurement-scale"; + public static final ChannelTypeUID CHANNEL_PRESSUREMEASUREMENT_SCALE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PRESSUREMEASUREMENT_SCALE); + + // ProxyConfiguration Cluster + public static final String CHANNEL_NAME_PROXYCONFIGURATION_CONFIGURATIONLIST = "ConfigurationList"; + public static final String CHANNEL_LABEL_PROXYCONFIGURATION_CONFIGURATIONLIST = "Configuration List"; + public static final String CHANNEL_ID_PROXYCONFIGURATION_CONFIGURATIONLIST = "proxyconfiguration-configurationlist"; + public static final ChannelTypeUID CHANNEL_PROXYCONFIGURATION_CONFIGURATIONLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PROXYCONFIGURATION_CONFIGURATIONLIST); + + // ProxyDiscovery Cluster + // PumpConfigurationAndControl Cluster + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXPRESSURE = "MaxPressure"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXPRESSURE = "Max Pressure"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXPRESSURE = "pumpconfigurationandcontrol-maxpressure"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXPRESSURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXPRESSURE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXSPEED = "MaxSpeed"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXSPEED = "Max Speed"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXSPEED = "pumpconfigurationandcontrol-maxspeed"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXSPEED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXSPEED); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXFLOW = "MaxFlow"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXFLOW = "Max Flow"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXFLOW = "pumpconfigurationandcontrol-maxflow"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXFLOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXFLOW); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MINCONSTPRESSURE = "MinConstPressure"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTPRESSURE = "Min Const Pressure"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTPRESSURE = "pumpconfigurationandcontrol-minconstpressure"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTPRESSURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTPRESSURE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXCONSTPRESSURE = "MaxConstPressure"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTPRESSURE = "Max Const Pressure"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTPRESSURE = "pumpconfigurationandcontrol-maxconstpressure"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTPRESSURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTPRESSURE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MINCOMPPRESSURE = "MinCompPressure"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MINCOMPPRESSURE = "Min Comp Pressure"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCOMPPRESSURE = "pumpconfigurationandcontrol-mincomppressure"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MINCOMPPRESSURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCOMPPRESSURE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXCOMPPRESSURE = "MaxCompPressure"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXCOMPPRESSURE = "Max Comp Pressure"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCOMPPRESSURE = "pumpconfigurationandcontrol-maxcomppressure"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXCOMPPRESSURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCOMPPRESSURE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MINCONSTSPEED = "MinConstSpeed"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTSPEED = "Min Const Speed"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTSPEED = "pumpconfigurationandcontrol-minconstspeed"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTSPEED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTSPEED); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXCONSTSPEED = "MaxConstSpeed"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTSPEED = "Max Const Speed"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTSPEED = "pumpconfigurationandcontrol-maxconstspeed"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTSPEED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTSPEED); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MINCONSTFLOW = "MinConstFlow"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTFLOW = "Min Const Flow"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTFLOW = "pumpconfigurationandcontrol-minconstflow"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTFLOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTFLOW); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXCONSTFLOW = "MaxConstFlow"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTFLOW = "Max Const Flow"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTFLOW = "pumpconfigurationandcontrol-maxconstflow"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTFLOW = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTFLOW); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MINCONSTTEMP = "MinConstTemp"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTTEMP = "Min Const Temp"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTTEMP = "pumpconfigurationandcontrol-minconsttemp"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MINCONSTTEMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MINCONSTTEMP); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_MAXCONSTTEMP = "MaxConstTemp"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTTEMP = "Max Const Temp"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTTEMP = "pumpconfigurationandcontrol-maxconsttemp"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_MAXCONSTTEMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_MAXCONSTTEMP); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_PUMPSTATUS = "PumpStatus"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_PUMPSTATUS = "Pump Status"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_PUMPSTATUS = "pumpconfigurationandcontrol-pumpstatus"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_PUMPSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_PUMPSTATUS); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_EFFECTIVEOPERATIONMODE = "EffectiveOperationMode"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_EFFECTIVEOPERATIONMODE = "Effective Operation Mode"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_EFFECTIVEOPERATIONMODE = "pumpconfigurationandcontrol-effectiveoperationmode"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_EFFECTIVEOPERATIONMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_EFFECTIVEOPERATIONMODE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_EFFECTIVECONTROLMODE = "EffectiveControlMode"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_EFFECTIVECONTROLMODE = "Effective Control Mode"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_EFFECTIVECONTROLMODE = "pumpconfigurationandcontrol-effectivecontrolmode"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_EFFECTIVECONTROLMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_EFFECTIVECONTROLMODE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_CAPACITY = "Capacity"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_CAPACITY = "Capacity"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_CAPACITY = "pumpconfigurationandcontrol-capacity"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_CAPACITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_CAPACITY); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_SPEED = "Speed"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_SPEED = "Speed"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_SPEED = "pumpconfigurationandcontrol-speed"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_SPEED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_SPEED); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_LIFETIMERUNNINGHOURS = "LifetimeRunningHours"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_LIFETIMERUNNINGHOURS = "Lifetime Running Hours"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_LIFETIMERUNNINGHOURS = "pumpconfigurationandcontrol-lifetimerunninghours"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_LIFETIMERUNNINGHOURS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_LIFETIMERUNNINGHOURS); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_POWER = "Power"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_POWER = "Power"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_POWER = "pumpconfigurationandcontrol-power"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_POWER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_POWER); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_LIFETIMEENERGYCONSUMED = "LifetimeEnergyConsumed"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_LIFETIMEENERGYCONSUMED = "Lifetime Energy Consumed"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_LIFETIMEENERGYCONSUMED = "pumpconfigurationandcontrol-lifetimeenergyconsumed"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_LIFETIMEENERGYCONSUMED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_LIFETIMEENERGYCONSUMED); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_OPERATIONMODE = "OperationMode"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_OPERATIONMODE = "Operation Mode"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_OPERATIONMODE = "pumpconfigurationandcontrol-operationmode"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_OPERATIONMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_OPERATIONMODE); + + public static final String CHANNEL_NAME_PUMPCONFIGURATIONANDCONTROL_CONTROLMODE = "ControlMode"; + public static final String CHANNEL_LABEL_PUMPCONFIGURATIONANDCONTROL_CONTROLMODE = "Control Mode"; + public static final String CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_CONTROLMODE = "pumpconfigurationandcontrol-controlmode"; + public static final ChannelTypeUID CHANNEL_PUMPCONFIGURATIONANDCONTROL_CONTROLMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_PUMPCONFIGURATIONANDCONTROL_CONTROLMODE); + + // RadonConcentrationMeasurement Cluster + // RefrigeratorAlarm Cluster + // RefrigeratorAndTemperatureControlledCabinetMode Cluster + // RelativeHumidityMeasurement Cluster + public static final String CHANNEL_NAME_RELATIVEHUMIDITYMEASUREMENT_MEASUREDVALUE = "MeasuredValue"; + public static final String CHANNEL_LABEL_RELATIVEHUMIDITYMEASUREMENT_MEASUREDVALUE = "Measured Value"; + public static final String CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_MEASUREDVALUE = "relativehumiditymeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_RELATIVEHUMIDITYMEASUREMENT_MEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_MEASUREDVALUE); + + public static final String CHANNEL_NAME_RELATIVEHUMIDITYMEASUREMENT_MINMEASUREDVALUE = "MinMeasuredValue"; + public static final String CHANNEL_LABEL_RELATIVEHUMIDITYMEASUREMENT_MINMEASUREDVALUE = "Min Measured Value"; + public static final String CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_MINMEASUREDVALUE = "relativehumiditymeasurement-minmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_RELATIVEHUMIDITYMEASUREMENT_MINMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_MINMEASUREDVALUE); + + public static final String CHANNEL_NAME_RELATIVEHUMIDITYMEASUREMENT_MAXMEASUREDVALUE = "MaxMeasuredValue"; + public static final String CHANNEL_LABEL_RELATIVEHUMIDITYMEASUREMENT_MAXMEASUREDVALUE = "Max Measured Value"; + public static final String CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_MAXMEASUREDVALUE = "relativehumiditymeasurement-maxmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_RELATIVEHUMIDITYMEASUREMENT_MAXMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_MAXMEASUREDVALUE); + + public static final String CHANNEL_NAME_RELATIVEHUMIDITYMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_LABEL_RELATIVEHUMIDITYMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_TOLERANCE = "relativehumiditymeasurement-tolerance"; + public static final ChannelTypeUID CHANNEL_RELATIVEHUMIDITYMEASUREMENT_TOLERANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RELATIVEHUMIDITYMEASUREMENT_TOLERANCE); + + // ResourceMonitoring Cluster + public static final String CHANNEL_NAME_RESOURCEMONITORING_CONDITION = "Condition"; + public static final String CHANNEL_LABEL_RESOURCEMONITORING_CONDITION = "Condition"; + public static final String CHANNEL_ID_RESOURCEMONITORING_CONDITION = "resourcemonitoring-condition"; + public static final ChannelTypeUID CHANNEL_RESOURCEMONITORING_CONDITION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RESOURCEMONITORING_CONDITION); + + public static final String CHANNEL_NAME_RESOURCEMONITORING_DEGRADATIONDIRECTION = "DegradationDirection"; + public static final String CHANNEL_LABEL_RESOURCEMONITORING_DEGRADATIONDIRECTION = "Degradation Direction"; + public static final String CHANNEL_ID_RESOURCEMONITORING_DEGRADATIONDIRECTION = "resourcemonitoring-degradationdirection"; + public static final ChannelTypeUID CHANNEL_RESOURCEMONITORING_DEGRADATIONDIRECTION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RESOURCEMONITORING_DEGRADATIONDIRECTION); + + public static final String CHANNEL_NAME_RESOURCEMONITORING_CHANGEINDICATION = "ChangeIndication"; + public static final String CHANNEL_LABEL_RESOURCEMONITORING_CHANGEINDICATION = "Change Indication"; + public static final String CHANNEL_ID_RESOURCEMONITORING_CHANGEINDICATION = "resourcemonitoring-changeindication"; + public static final ChannelTypeUID CHANNEL_RESOURCEMONITORING_CHANGEINDICATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RESOURCEMONITORING_CHANGEINDICATION); + + public static final String CHANNEL_NAME_RESOURCEMONITORING_INPLACEINDICATOR = "InPlaceIndicator"; + public static final String CHANNEL_LABEL_RESOURCEMONITORING_INPLACEINDICATOR = "In Place Indicator"; + public static final String CHANNEL_ID_RESOURCEMONITORING_INPLACEINDICATOR = "resourcemonitoring-inplaceindicator"; + public static final ChannelTypeUID CHANNEL_RESOURCEMONITORING_INPLACEINDICATOR = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RESOURCEMONITORING_INPLACEINDICATOR); + + public static final String CHANNEL_NAME_RESOURCEMONITORING_LASTCHANGEDTIME = "LastChangedTime"; + public static final String CHANNEL_LABEL_RESOURCEMONITORING_LASTCHANGEDTIME = "Last Changed Time"; + public static final String CHANNEL_ID_RESOURCEMONITORING_LASTCHANGEDTIME = "resourcemonitoring-lastchangedtime"; + public static final ChannelTypeUID CHANNEL_RESOURCEMONITORING_LASTCHANGEDTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RESOURCEMONITORING_LASTCHANGEDTIME); + + public static final String CHANNEL_NAME_RESOURCEMONITORING_REPLACEMENTPRODUCTLIST = "ReplacementProductList"; + public static final String CHANNEL_LABEL_RESOURCEMONITORING_REPLACEMENTPRODUCTLIST = "Replacement Product List"; + public static final String CHANNEL_ID_RESOURCEMONITORING_REPLACEMENTPRODUCTLIST = "resourcemonitoring-replacementproductlist"; + public static final ChannelTypeUID CHANNEL_RESOURCEMONITORING_REPLACEMENTPRODUCTLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_RESOURCEMONITORING_REPLACEMENTPRODUCTLIST); + + // RvcCleanMode Cluster + // RvcOperationalState Cluster + // RvcRunMode Cluster + // ScenesManagement Cluster + public static final String CHANNEL_NAME_SCENESMANAGEMENT_LASTCONFIGUREDBY = "LastConfiguredBy"; + public static final String CHANNEL_LABEL_SCENESMANAGEMENT_LASTCONFIGUREDBY = "Last Configured By"; + public static final String CHANNEL_ID_SCENESMANAGEMENT_LASTCONFIGUREDBY = "scenesmanagement-lastconfiguredby"; + public static final ChannelTypeUID CHANNEL_SCENESMANAGEMENT_LASTCONFIGUREDBY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SCENESMANAGEMENT_LASTCONFIGUREDBY); + + public static final String CHANNEL_NAME_SCENESMANAGEMENT_SCENETABLESIZE = "SceneTableSize"; + public static final String CHANNEL_LABEL_SCENESMANAGEMENT_SCENETABLESIZE = "Scene Table Size"; + public static final String CHANNEL_ID_SCENESMANAGEMENT_SCENETABLESIZE = "scenesmanagement-scenetablesize"; + public static final ChannelTypeUID CHANNEL_SCENESMANAGEMENT_SCENETABLESIZE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SCENESMANAGEMENT_SCENETABLESIZE); + + public static final String CHANNEL_NAME_SCENESMANAGEMENT_FABRICSCENEINFO = "FabricSceneInfo"; + public static final String CHANNEL_LABEL_SCENESMANAGEMENT_FABRICSCENEINFO = "Fabric Scene Info"; + public static final String CHANNEL_ID_SCENESMANAGEMENT_FABRICSCENEINFO = "scenesmanagement-fabricsceneinfo"; + public static final ChannelTypeUID CHANNEL_SCENESMANAGEMENT_FABRICSCENEINFO = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SCENESMANAGEMENT_FABRICSCENEINFO); + + // ServiceArea Cluster + public static final String CHANNEL_NAME_SERVICEAREA_SUPPORTEDAREAS = "SupportedAreas"; + public static final String CHANNEL_LABEL_SERVICEAREA_SUPPORTEDAREAS = "Supported Areas"; + public static final String CHANNEL_ID_SERVICEAREA_SUPPORTEDAREAS = "servicearea-supportedareas"; + public static final ChannelTypeUID CHANNEL_SERVICEAREA_SUPPORTEDAREAS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SERVICEAREA_SUPPORTEDAREAS); + + public static final String CHANNEL_NAME_SERVICEAREA_SUPPORTEDMAPS = "SupportedMaps"; + public static final String CHANNEL_LABEL_SERVICEAREA_SUPPORTEDMAPS = "Supported Maps"; + public static final String CHANNEL_ID_SERVICEAREA_SUPPORTEDMAPS = "servicearea-supportedmaps"; + public static final ChannelTypeUID CHANNEL_SERVICEAREA_SUPPORTEDMAPS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SERVICEAREA_SUPPORTEDMAPS); + + public static final String CHANNEL_NAME_SERVICEAREA_SELECTEDAREAS = "SelectedAreas"; + public static final String CHANNEL_LABEL_SERVICEAREA_SELECTEDAREAS = "Selected Areas"; + public static final String CHANNEL_ID_SERVICEAREA_SELECTEDAREAS = "servicearea-selectedareas"; + public static final ChannelTypeUID CHANNEL_SERVICEAREA_SELECTEDAREAS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SERVICEAREA_SELECTEDAREAS); + + public static final String CHANNEL_NAME_SERVICEAREA_CURRENTAREA = "CurrentArea"; + public static final String CHANNEL_LABEL_SERVICEAREA_CURRENTAREA = "Current Area"; + public static final String CHANNEL_ID_SERVICEAREA_CURRENTAREA = "servicearea-currentarea"; + public static final ChannelTypeUID CHANNEL_SERVICEAREA_CURRENTAREA = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SERVICEAREA_CURRENTAREA); + + public static final String CHANNEL_NAME_SERVICEAREA_ESTIMATEDENDTIME = "EstimatedEndTime"; + public static final String CHANNEL_LABEL_SERVICEAREA_ESTIMATEDENDTIME = "Estimated End Time"; + public static final String CHANNEL_ID_SERVICEAREA_ESTIMATEDENDTIME = "servicearea-estimatedendtime"; + public static final ChannelTypeUID CHANNEL_SERVICEAREA_ESTIMATEDENDTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SERVICEAREA_ESTIMATEDENDTIME); + + public static final String CHANNEL_NAME_SERVICEAREA_PROGRESS = "Progress"; + public static final String CHANNEL_LABEL_SERVICEAREA_PROGRESS = "Progress"; + public static final String CHANNEL_ID_SERVICEAREA_PROGRESS = "servicearea-progress"; + public static final ChannelTypeUID CHANNEL_SERVICEAREA_PROGRESS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SERVICEAREA_PROGRESS); + + // SmokeCoAlarm Cluster + public static final String CHANNEL_NAME_SMOKECOALARM_EXPRESSEDSTATE = "ExpressedState"; + public static final String CHANNEL_LABEL_SMOKECOALARM_EXPRESSEDSTATE = "Expressed State"; + public static final String CHANNEL_ID_SMOKECOALARM_EXPRESSEDSTATE = "smokecoalarm-expressedstate"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_EXPRESSEDSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_EXPRESSEDSTATE); + + public static final String CHANNEL_NAME_SMOKECOALARM_SMOKESTATE = "SmokeState"; + public static final String CHANNEL_LABEL_SMOKECOALARM_SMOKESTATE = "Smoke State"; + public static final String CHANNEL_ID_SMOKECOALARM_SMOKESTATE = "smokecoalarm-smokestate"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_SMOKESTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_SMOKESTATE); + + public static final String CHANNEL_NAME_SMOKECOALARM_COSTATE = "CoState"; + public static final String CHANNEL_LABEL_SMOKECOALARM_COSTATE = "Co State"; + public static final String CHANNEL_ID_SMOKECOALARM_COSTATE = "smokecoalarm-costate"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_COSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_COSTATE); + + public static final String CHANNEL_NAME_SMOKECOALARM_BATTERYALERT = "BatteryAlert"; + public static final String CHANNEL_LABEL_SMOKECOALARM_BATTERYALERT = "Battery Alert"; + public static final String CHANNEL_ID_SMOKECOALARM_BATTERYALERT = "smokecoalarm-batteryalert"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_BATTERYALERT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_BATTERYALERT); + + public static final String CHANNEL_NAME_SMOKECOALARM_DEVICEMUTED = "DeviceMuted"; + public static final String CHANNEL_LABEL_SMOKECOALARM_DEVICEMUTED = "Device Muted"; + public static final String CHANNEL_ID_SMOKECOALARM_DEVICEMUTED = "smokecoalarm-devicemuted"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_DEVICEMUTED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_DEVICEMUTED); + + public static final String CHANNEL_NAME_SMOKECOALARM_TESTINPROGRESS = "TestInProgress"; + public static final String CHANNEL_LABEL_SMOKECOALARM_TESTINPROGRESS = "Test In Progress"; + public static final String CHANNEL_ID_SMOKECOALARM_TESTINPROGRESS = "smokecoalarm-testinprogress"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_TESTINPROGRESS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_TESTINPROGRESS); + + public static final String CHANNEL_NAME_SMOKECOALARM_HARDWAREFAULTALERT = "HardwareFaultAlert"; + public static final String CHANNEL_LABEL_SMOKECOALARM_HARDWAREFAULTALERT = "Hardware Fault Alert"; + public static final String CHANNEL_ID_SMOKECOALARM_HARDWAREFAULTALERT = "smokecoalarm-hardwarefaultalert"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_HARDWAREFAULTALERT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_HARDWAREFAULTALERT); + + public static final String CHANNEL_NAME_SMOKECOALARM_ENDOFSERVICEALERT = "EndOfServiceAlert"; + public static final String CHANNEL_LABEL_SMOKECOALARM_ENDOFSERVICEALERT = "End Of Service Alert"; + public static final String CHANNEL_ID_SMOKECOALARM_ENDOFSERVICEALERT = "smokecoalarm-endofservicealert"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_ENDOFSERVICEALERT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_ENDOFSERVICEALERT); + + public static final String CHANNEL_NAME_SMOKECOALARM_INTERCONNECTSMOKEALARM = "InterconnectSmokeAlarm"; + public static final String CHANNEL_LABEL_SMOKECOALARM_INTERCONNECTSMOKEALARM = "Interconnect Smoke Alarm"; + public static final String CHANNEL_ID_SMOKECOALARM_INTERCONNECTSMOKEALARM = "smokecoalarm-interconnectsmokealarm"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_INTERCONNECTSMOKEALARM = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_INTERCONNECTSMOKEALARM); + + public static final String CHANNEL_NAME_SMOKECOALARM_INTERCONNECTCOALARM = "InterconnectCoAlarm"; + public static final String CHANNEL_LABEL_SMOKECOALARM_INTERCONNECTCOALARM = "Interconnect Co Alarm"; + public static final String CHANNEL_ID_SMOKECOALARM_INTERCONNECTCOALARM = "smokecoalarm-interconnectcoalarm"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_INTERCONNECTCOALARM = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_INTERCONNECTCOALARM); + + public static final String CHANNEL_NAME_SMOKECOALARM_CONTAMINATIONSTATE = "ContaminationState"; + public static final String CHANNEL_LABEL_SMOKECOALARM_CONTAMINATIONSTATE = "Contamination State"; + public static final String CHANNEL_ID_SMOKECOALARM_CONTAMINATIONSTATE = "smokecoalarm-contaminationstate"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_CONTAMINATIONSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_CONTAMINATIONSTATE); + + public static final String CHANNEL_NAME_SMOKECOALARM_SMOKESENSITIVITYLEVEL = "SmokeSensitivityLevel"; + public static final String CHANNEL_LABEL_SMOKECOALARM_SMOKESENSITIVITYLEVEL = "Smoke Sensitivity Level"; + public static final String CHANNEL_ID_SMOKECOALARM_SMOKESENSITIVITYLEVEL = "smokecoalarm-smokesensitivitylevel"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_SMOKESENSITIVITYLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_SMOKESENSITIVITYLEVEL); + + public static final String CHANNEL_NAME_SMOKECOALARM_EXPIRYDATE = "ExpiryDate"; + public static final String CHANNEL_LABEL_SMOKECOALARM_EXPIRYDATE = "Expiry Date"; + public static final String CHANNEL_ID_SMOKECOALARM_EXPIRYDATE = "smokecoalarm-expirydate"; + public static final ChannelTypeUID CHANNEL_SMOKECOALARM_EXPIRYDATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SMOKECOALARM_EXPIRYDATE); + + // SoftwareDiagnostics Cluster + public static final String CHANNEL_NAME_SOFTWAREDIAGNOSTICS_THREADMETRICS = "ThreadMetrics"; + public static final String CHANNEL_LABEL_SOFTWAREDIAGNOSTICS_THREADMETRICS = "Thread Metrics"; + public static final String CHANNEL_ID_SOFTWAREDIAGNOSTICS_THREADMETRICS = "softwarediagnostics-threadmetrics"; + public static final ChannelTypeUID CHANNEL_SOFTWAREDIAGNOSTICS_THREADMETRICS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SOFTWAREDIAGNOSTICS_THREADMETRICS); + + public static final String CHANNEL_NAME_SOFTWAREDIAGNOSTICS_CURRENTHEAPFREE = "CurrentHeapFree"; + public static final String CHANNEL_LABEL_SOFTWAREDIAGNOSTICS_CURRENTHEAPFREE = "Current Heap Free"; + public static final String CHANNEL_ID_SOFTWAREDIAGNOSTICS_CURRENTHEAPFREE = "softwarediagnostics-currentheapfree"; + public static final ChannelTypeUID CHANNEL_SOFTWAREDIAGNOSTICS_CURRENTHEAPFREE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SOFTWAREDIAGNOSTICS_CURRENTHEAPFREE); + + public static final String CHANNEL_NAME_SOFTWAREDIAGNOSTICS_CURRENTHEAPUSED = "CurrentHeapUsed"; + public static final String CHANNEL_LABEL_SOFTWAREDIAGNOSTICS_CURRENTHEAPUSED = "Current Heap Used"; + public static final String CHANNEL_ID_SOFTWAREDIAGNOSTICS_CURRENTHEAPUSED = "softwarediagnostics-currentheapused"; + public static final ChannelTypeUID CHANNEL_SOFTWAREDIAGNOSTICS_CURRENTHEAPUSED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SOFTWAREDIAGNOSTICS_CURRENTHEAPUSED); + + public static final String CHANNEL_NAME_SOFTWAREDIAGNOSTICS_CURRENTHEAPHIGHWATERMARK = "CurrentHeapHighWatermark"; + public static final String CHANNEL_LABEL_SOFTWAREDIAGNOSTICS_CURRENTHEAPHIGHWATERMARK = "Current Heap High Watermark"; + public static final String CHANNEL_ID_SOFTWAREDIAGNOSTICS_CURRENTHEAPHIGHWATERMARK = "softwarediagnostics-currentheaphighwatermark"; + public static final ChannelTypeUID CHANNEL_SOFTWAREDIAGNOSTICS_CURRENTHEAPHIGHWATERMARK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SOFTWAREDIAGNOSTICS_CURRENTHEAPHIGHWATERMARK); + + // Switch Cluster + public static final String CHANNEL_NAME_SWITCH_NUMBEROFPOSITIONS = "NumberOfPositions"; + public static final String CHANNEL_LABEL_SWITCH_NUMBEROFPOSITIONS = "Number Of Positions"; + public static final String CHANNEL_ID_SWITCH_NUMBEROFPOSITIONS = "switch-numberofpositions"; + public static final ChannelTypeUID CHANNEL_SWITCH_NUMBEROFPOSITIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SWITCH_NUMBEROFPOSITIONS); + + public static final String CHANNEL_NAME_SWITCH_CURRENTPOSITION = "CurrentPosition"; + public static final String CHANNEL_LABEL_SWITCH_CURRENTPOSITION = "Current Position"; + public static final String CHANNEL_ID_SWITCH_CURRENTPOSITION = "switch-currentposition"; + public static final ChannelTypeUID CHANNEL_SWITCH_CURRENTPOSITION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SWITCH_CURRENTPOSITION); + + public static final String CHANNEL_NAME_SWITCH_MULTIPRESSMAX = "MultiPressMax"; + public static final String CHANNEL_LABEL_SWITCH_MULTIPRESSMAX = "Multi Press Max"; + public static final String CHANNEL_ID_SWITCH_MULTIPRESSMAX = "switch-multipressmax"; + public static final ChannelTypeUID CHANNEL_SWITCH_MULTIPRESSMAX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_SWITCH_MULTIPRESSMAX); + + // TargetNavigator Cluster + public static final String CHANNEL_NAME_TARGETNAVIGATOR_TARGETLIST = "TargetList"; + public static final String CHANNEL_LABEL_TARGETNAVIGATOR_TARGETLIST = "Target List"; + public static final String CHANNEL_ID_TARGETNAVIGATOR_TARGETLIST = "targetnavigator-targetlist"; + public static final ChannelTypeUID CHANNEL_TARGETNAVIGATOR_TARGETLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TARGETNAVIGATOR_TARGETLIST); + + public static final String CHANNEL_NAME_TARGETNAVIGATOR_CURRENTTARGET = "CurrentTarget"; + public static final String CHANNEL_LABEL_TARGETNAVIGATOR_CURRENTTARGET = "Current Target"; + public static final String CHANNEL_ID_TARGETNAVIGATOR_CURRENTTARGET = "targetnavigator-currenttarget"; + public static final ChannelTypeUID CHANNEL_TARGETNAVIGATOR_CURRENTTARGET = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TARGETNAVIGATOR_CURRENTTARGET); + + // TemperatureControl Cluster + public static final String CHANNEL_NAME_TEMPERATURECONTROL_TEMPERATURESETPOINT = "TemperatureSetpoint"; + public static final String CHANNEL_LABEL_TEMPERATURECONTROL_TEMPERATURESETPOINT = "Temperature Setpoint"; + public static final String CHANNEL_ID_TEMPERATURECONTROL_TEMPERATURESETPOINT = "temperaturecontrol-temperaturesetpoint"; + public static final ChannelTypeUID CHANNEL_TEMPERATURECONTROL_TEMPERATURESETPOINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATURECONTROL_TEMPERATURESETPOINT); + + public static final String CHANNEL_NAME_TEMPERATURECONTROL_MINTEMPERATURE = "MinTemperature"; + public static final String CHANNEL_LABEL_TEMPERATURECONTROL_MINTEMPERATURE = "Min Temperature"; + public static final String CHANNEL_ID_TEMPERATURECONTROL_MINTEMPERATURE = "temperaturecontrol-mintemperature"; + public static final ChannelTypeUID CHANNEL_TEMPERATURECONTROL_MINTEMPERATURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATURECONTROL_MINTEMPERATURE); + + public static final String CHANNEL_NAME_TEMPERATURECONTROL_MAXTEMPERATURE = "MaxTemperature"; + public static final String CHANNEL_LABEL_TEMPERATURECONTROL_MAXTEMPERATURE = "Max Temperature"; + public static final String CHANNEL_ID_TEMPERATURECONTROL_MAXTEMPERATURE = "temperaturecontrol-maxtemperature"; + public static final ChannelTypeUID CHANNEL_TEMPERATURECONTROL_MAXTEMPERATURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATURECONTROL_MAXTEMPERATURE); + + public static final String CHANNEL_NAME_TEMPERATURECONTROL_STEP = "Step"; + public static final String CHANNEL_LABEL_TEMPERATURECONTROL_STEP = "Step"; + public static final String CHANNEL_ID_TEMPERATURECONTROL_STEP = "temperaturecontrol-step"; + public static final ChannelTypeUID CHANNEL_TEMPERATURECONTROL_STEP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATURECONTROL_STEP); + + public static final String CHANNEL_NAME_TEMPERATURECONTROL_SELECTEDTEMPERATURELEVEL = "SelectedTemperatureLevel"; + public static final String CHANNEL_LABEL_TEMPERATURECONTROL_SELECTEDTEMPERATURELEVEL = "Selected Temperature Level"; + public static final String CHANNEL_ID_TEMPERATURECONTROL_SELECTEDTEMPERATURELEVEL = "temperaturecontrol-selectedtemperaturelevel"; + public static final ChannelTypeUID CHANNEL_TEMPERATURECONTROL_SELECTEDTEMPERATURELEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATURECONTROL_SELECTEDTEMPERATURELEVEL); + + public static final String CHANNEL_NAME_TEMPERATURECONTROL_SUPPORTEDTEMPERATURELEVELS = "SupportedTemperatureLevels"; + public static final String CHANNEL_LABEL_TEMPERATURECONTROL_SUPPORTEDTEMPERATURELEVELS = "Supported Temperature Levels"; + public static final String CHANNEL_ID_TEMPERATURECONTROL_SUPPORTEDTEMPERATURELEVELS = "temperaturecontrol-supportedtemperaturelevels"; + public static final ChannelTypeUID CHANNEL_TEMPERATURECONTROL_SUPPORTEDTEMPERATURELEVELS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATURECONTROL_SUPPORTEDTEMPERATURELEVELS); + + // TemperatureMeasurement Cluster + public static final String CHANNEL_NAME_TEMPERATUREMEASUREMENT_MEASUREDVALUE = "MeasuredValue"; + public static final String CHANNEL_LABEL_TEMPERATUREMEASUREMENT_MEASUREDVALUE = "Measured Value"; + public static final String CHANNEL_ID_TEMPERATUREMEASUREMENT_MEASUREDVALUE = "temperaturemeasurement-measuredvalue"; + public static final ChannelTypeUID CHANNEL_TEMPERATUREMEASUREMENT_MEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATUREMEASUREMENT_MEASUREDVALUE); + + public static final String CHANNEL_NAME_TEMPERATUREMEASUREMENT_MINMEASUREDVALUE = "MinMeasuredValue"; + public static final String CHANNEL_LABEL_TEMPERATUREMEASUREMENT_MINMEASUREDVALUE = "Min Measured Value"; + public static final String CHANNEL_ID_TEMPERATUREMEASUREMENT_MINMEASUREDVALUE = "temperaturemeasurement-minmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_TEMPERATUREMEASUREMENT_MINMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATUREMEASUREMENT_MINMEASUREDVALUE); + + public static final String CHANNEL_NAME_TEMPERATUREMEASUREMENT_MAXMEASUREDVALUE = "MaxMeasuredValue"; + public static final String CHANNEL_LABEL_TEMPERATUREMEASUREMENT_MAXMEASUREDVALUE = "Max Measured Value"; + public static final String CHANNEL_ID_TEMPERATUREMEASUREMENT_MAXMEASUREDVALUE = "temperaturemeasurement-maxmeasuredvalue"; + public static final ChannelTypeUID CHANNEL_TEMPERATUREMEASUREMENT_MAXMEASUREDVALUE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATUREMEASUREMENT_MAXMEASUREDVALUE); + + public static final String CHANNEL_NAME_TEMPERATUREMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_LABEL_TEMPERATUREMEASUREMENT_TOLERANCE = "Tolerance"; + public static final String CHANNEL_ID_TEMPERATUREMEASUREMENT_TOLERANCE = "temperaturemeasurement-tolerance"; + public static final ChannelTypeUID CHANNEL_TEMPERATUREMEASUREMENT_TOLERANCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TEMPERATUREMEASUREMENT_TOLERANCE); + + // Thermostat Cluster + public static final String CHANNEL_NAME_THERMOSTAT_LOCALTEMPERATURE = "LocalTemperature"; + public static final String CHANNEL_LABEL_THERMOSTAT_LOCALTEMPERATURE = "Local Temperature"; + public static final String CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE = "thermostat-localtemperature"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_LOCALTEMPERATURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE); + + public static final String CHANNEL_NAME_THERMOSTAT_OUTDOORTEMPERATURE = "OutdoorTemperature"; + public static final String CHANNEL_LABEL_THERMOSTAT_OUTDOORTEMPERATURE = "Outdoor Temperature"; + public static final String CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE = "thermostat-outdoortemperature"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OUTDOORTEMPERATURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE); + + public static final String CHANNEL_NAME_THERMOSTAT_OCCUPANCY = "Occupancy"; + public static final String CHANNEL_LABEL_THERMOSTAT_OCCUPANCY = "Occupancy"; + public static final String CHANNEL_ID_THERMOSTAT_OCCUPANCY = "thermostat-occupancy"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPANCY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OCCUPANCY); + + public static final String CHANNEL_NAME_THERMOSTAT_ABSMINHEATSETPOINTLIMIT = "AbsMinHeatSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_ABSMINHEATSETPOINTLIMIT = "Abs Min Heat Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_ABSMINHEATSETPOINTLIMIT = "thermostat-absminheatsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ABSMINHEATSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ABSMINHEATSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_ABSMAXHEATSETPOINTLIMIT = "AbsMaxHeatSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_ABSMAXHEATSETPOINTLIMIT = "Abs Max Heat Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_ABSMAXHEATSETPOINTLIMIT = "thermostat-absmaxheatsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ABSMAXHEATSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ABSMAXHEATSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_ABSMINCOOLSETPOINTLIMIT = "AbsMinCoolSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_ABSMINCOOLSETPOINTLIMIT = "Abs Min Cool Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_ABSMINCOOLSETPOINTLIMIT = "thermostat-absmincoolsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ABSMINCOOLSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ABSMINCOOLSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_ABSMAXCOOLSETPOINTLIMIT = "AbsMaxCoolSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_ABSMAXCOOLSETPOINTLIMIT = "Abs Max Cool Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_ABSMAXCOOLSETPOINTLIMIT = "thermostat-absmaxcoolsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ABSMAXCOOLSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ABSMAXCOOLSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_PICOOLINGDEMAND = "PiCoolingDemand"; + public static final String CHANNEL_LABEL_THERMOSTAT_PICOOLINGDEMAND = "Pi Cooling Demand"; + public static final String CHANNEL_ID_THERMOSTAT_PICOOLINGDEMAND = "thermostat-picoolingdemand"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_PICOOLINGDEMAND = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_PICOOLINGDEMAND); + + public static final String CHANNEL_NAME_THERMOSTAT_PIHEATINGDEMAND = "PiHeatingDemand"; + public static final String CHANNEL_LABEL_THERMOSTAT_PIHEATINGDEMAND = "Pi Heating Demand"; + public static final String CHANNEL_ID_THERMOSTAT_PIHEATINGDEMAND = "thermostat-piheatingdemand"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_PIHEATINGDEMAND = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_PIHEATINGDEMAND); + + public static final String CHANNEL_NAME_THERMOSTAT_LOCALTEMPERATURECALIBRATION = "LocalTemperatureCalibration"; + public static final String CHANNEL_LABEL_THERMOSTAT_LOCALTEMPERATURECALIBRATION = "Local Temperature Calibration"; + public static final String CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURECALIBRATION = "thermostat-localtemperaturecalibration"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_LOCALTEMPERATURECALIBRATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURECALIBRATION); + + public static final String CHANNEL_NAME_THERMOSTAT_OCCUPIEDCOOLINGSETPOINT = "OccupiedCoolingSetpoint"; + public static final String CHANNEL_LABEL_THERMOSTAT_OCCUPIEDCOOLINGSETPOINT = "Occupied Cooling Setpoint"; + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLINGSETPOINT = "thermostat-occupiedcoolingsetpoint"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDCOOLINGSETPOINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLINGSETPOINT); + + public static final String CHANNEL_NAME_THERMOSTAT_OCCUPIEDHEATINGSETPOINT = "OccupiedHeatingSetpoint"; + public static final String CHANNEL_LABEL_THERMOSTAT_OCCUPIEDHEATINGSETPOINT = "Occupied Heating Setpoint"; + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATINGSETPOINT = "thermostat-occupiedheatingsetpoint"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDHEATINGSETPOINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATINGSETPOINT); + + public static final String CHANNEL_NAME_THERMOSTAT_UNOCCUPIEDCOOLINGSETPOINT = "UnoccupiedCoolingSetpoint"; + public static final String CHANNEL_LABEL_THERMOSTAT_UNOCCUPIEDCOOLINGSETPOINT = "Unoccupied Cooling Setpoint"; + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLINGSETPOINT = "thermostat-unoccupiedcoolingsetpoint"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDCOOLINGSETPOINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLINGSETPOINT); + + public static final String CHANNEL_NAME_THERMOSTAT_UNOCCUPIEDHEATINGSETPOINT = "UnoccupiedHeatingSetpoint"; + public static final String CHANNEL_LABEL_THERMOSTAT_UNOCCUPIEDHEATINGSETPOINT = "Unoccupied Heating Setpoint"; + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATINGSETPOINT = "thermostat-unoccupiedheatingsetpoint"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDHEATINGSETPOINT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATINGSETPOINT); + + public static final String CHANNEL_NAME_THERMOSTAT_MINHEATSETPOINTLIMIT = "MinHeatSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_MINHEATSETPOINTLIMIT = "Min Heat Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_MINHEATSETPOINTLIMIT = "thermostat-minheatsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_MINHEATSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_MINHEATSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_MAXHEATSETPOINTLIMIT = "MaxHeatSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_MAXHEATSETPOINTLIMIT = "Max Heat Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_MAXHEATSETPOINTLIMIT = "thermostat-maxheatsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_MAXHEATSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_MAXHEATSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_MINCOOLSETPOINTLIMIT = "MinCoolSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_MINCOOLSETPOINTLIMIT = "Min Cool Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_MINCOOLSETPOINTLIMIT = "thermostat-mincoolsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_MINCOOLSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_MINCOOLSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_MAXCOOLSETPOINTLIMIT = "MaxCoolSetpointLimit"; + public static final String CHANNEL_LABEL_THERMOSTAT_MAXCOOLSETPOINTLIMIT = "Max Cool Setpoint Limit"; + public static final String CHANNEL_ID_THERMOSTAT_MAXCOOLSETPOINTLIMIT = "thermostat-maxcoolsetpointlimit"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_MAXCOOLSETPOINTLIMIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_MAXCOOLSETPOINTLIMIT); + + public static final String CHANNEL_NAME_THERMOSTAT_MINSETPOINTDEADBAND = "MinSetpointDeadBand"; + public static final String CHANNEL_LABEL_THERMOSTAT_MINSETPOINTDEADBAND = "Min Setpoint Dead Band"; + public static final String CHANNEL_ID_THERMOSTAT_MINSETPOINTDEADBAND = "thermostat-minsetpointdeadband"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_MINSETPOINTDEADBAND = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_MINSETPOINTDEADBAND); + + public static final String CHANNEL_NAME_THERMOSTAT_REMOTESENSING = "RemoteSensing"; + public static final String CHANNEL_LABEL_THERMOSTAT_REMOTESENSING = "Remote Sensing"; + public static final String CHANNEL_ID_THERMOSTAT_REMOTESENSING = "thermostat-remotesensing"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_REMOTESENSING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_REMOTESENSING); + + public static final String CHANNEL_NAME_THERMOSTAT_CONTROLSEQUENCEOFOPERATION = "ControlSequenceOfOperation"; + public static final String CHANNEL_LABEL_THERMOSTAT_CONTROLSEQUENCEOFOPERATION = "Control Sequence Of Operation"; + public static final String CHANNEL_ID_THERMOSTAT_CONTROLSEQUENCEOFOPERATION = "thermostat-controlsequenceofoperation"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_CONTROLSEQUENCEOFOPERATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_CONTROLSEQUENCEOFOPERATION); + + public static final String CHANNEL_NAME_THERMOSTAT_SYSTEMMODE = "SystemMode"; + public static final String CHANNEL_LABEL_THERMOSTAT_SYSTEMMODE = "System Mode"; + public static final String CHANNEL_ID_THERMOSTAT_SYSTEMMODE = "thermostat-systemmode"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SYSTEMMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SYSTEMMODE); + + public static final String CHANNEL_NAME_THERMOSTAT_THERMOSTATRUNNINGMODE = "ThermostatRunningMode"; + public static final String CHANNEL_LABEL_THERMOSTAT_THERMOSTATRUNNINGMODE = "Thermostat Running Mode"; + public static final String CHANNEL_ID_THERMOSTAT_THERMOSTATRUNNINGMODE = "thermostat-thermostatrunningmode"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_THERMOSTATRUNNINGMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_THERMOSTATRUNNINGMODE); + + public static final String CHANNEL_NAME_THERMOSTAT_STARTOFWEEK = "StartOfWeek"; + public static final String CHANNEL_LABEL_THERMOSTAT_STARTOFWEEK = "Start Of Week"; + public static final String CHANNEL_ID_THERMOSTAT_STARTOFWEEK = "thermostat-startofweek"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_STARTOFWEEK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_STARTOFWEEK); + + public static final String CHANNEL_NAME_THERMOSTAT_NUMBEROFWEEKLYTRANSITIONS = "NumberOfWeeklyTransitions"; + public static final String CHANNEL_LABEL_THERMOSTAT_NUMBEROFWEEKLYTRANSITIONS = "Number Of Weekly Transitions"; + public static final String CHANNEL_ID_THERMOSTAT_NUMBEROFWEEKLYTRANSITIONS = "thermostat-numberofweeklytransitions"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_NUMBEROFWEEKLYTRANSITIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_NUMBEROFWEEKLYTRANSITIONS); + + public static final String CHANNEL_NAME_THERMOSTAT_NUMBEROFDAILYTRANSITIONS = "NumberOfDailyTransitions"; + public static final String CHANNEL_LABEL_THERMOSTAT_NUMBEROFDAILYTRANSITIONS = "Number Of Daily Transitions"; + public static final String CHANNEL_ID_THERMOSTAT_NUMBEROFDAILYTRANSITIONS = "thermostat-numberofdailytransitions"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_NUMBEROFDAILYTRANSITIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_NUMBEROFDAILYTRANSITIONS); + + public static final String CHANNEL_NAME_THERMOSTAT_TEMPERATURESETPOINTHOLD = "TemperatureSetpointHold"; + public static final String CHANNEL_LABEL_THERMOSTAT_TEMPERATURESETPOINTHOLD = "Temperature Setpoint Hold"; + public static final String CHANNEL_ID_THERMOSTAT_TEMPERATURESETPOINTHOLD = "thermostat-temperaturesetpointhold"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_TEMPERATURESETPOINTHOLD = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_TEMPERATURESETPOINTHOLD); + + public static final String CHANNEL_NAME_THERMOSTAT_TEMPERATURESETPOINTHOLDDURATION = "TemperatureSetpointHoldDuration"; + public static final String CHANNEL_LABEL_THERMOSTAT_TEMPERATURESETPOINTHOLDDURATION = "Temperature Setpoint Hold Duration"; + public static final String CHANNEL_ID_THERMOSTAT_TEMPERATURESETPOINTHOLDDURATION = "thermostat-temperaturesetpointholdduration"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_TEMPERATURESETPOINTHOLDDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_TEMPERATURESETPOINTHOLDDURATION); + + public static final String CHANNEL_NAME_THERMOSTAT_THERMOSTATPROGRAMMINGOPERATIONMODE = "ThermostatProgrammingOperationMode"; + public static final String CHANNEL_LABEL_THERMOSTAT_THERMOSTATPROGRAMMINGOPERATIONMODE = "Thermostat Programming Operation Mode"; + public static final String CHANNEL_ID_THERMOSTAT_THERMOSTATPROGRAMMINGOPERATIONMODE = "thermostat-thermostatprogrammingoperationmode"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_THERMOSTATPROGRAMMINGOPERATIONMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_THERMOSTATPROGRAMMINGOPERATIONMODE); + + public static final String CHANNEL_NAME_THERMOSTAT_THERMOSTATRUNNINGSTATE = "ThermostatRunningState"; + public static final String CHANNEL_LABEL_THERMOSTAT_THERMOSTATRUNNINGSTATE = "Thermostat Running State"; + public static final String CHANNEL_ID_THERMOSTAT_THERMOSTATRUNNINGSTATE = "thermostat-thermostatrunningstate"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_THERMOSTATRUNNINGSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_THERMOSTATRUNNINGSTATE); + + public static final String CHANNEL_NAME_THERMOSTAT_SETPOINTCHANGESOURCE = "SetpointChangeSource"; + public static final String CHANNEL_LABEL_THERMOSTAT_SETPOINTCHANGESOURCE = "Setpoint Change Source"; + public static final String CHANNEL_ID_THERMOSTAT_SETPOINTCHANGESOURCE = "thermostat-setpointchangesource"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SETPOINTCHANGESOURCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SETPOINTCHANGESOURCE); + + public static final String CHANNEL_NAME_THERMOSTAT_SETPOINTCHANGEAMOUNT = "SetpointChangeAmount"; + public static final String CHANNEL_LABEL_THERMOSTAT_SETPOINTCHANGEAMOUNT = "Setpoint Change Amount"; + public static final String CHANNEL_ID_THERMOSTAT_SETPOINTCHANGEAMOUNT = "thermostat-setpointchangeamount"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SETPOINTCHANGEAMOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SETPOINTCHANGEAMOUNT); + + public static final String CHANNEL_NAME_THERMOSTAT_SETPOINTCHANGESOURCETIMESTAMP = "SetpointChangeSourceTimestamp"; + public static final String CHANNEL_LABEL_THERMOSTAT_SETPOINTCHANGESOURCETIMESTAMP = "Setpoint Change Source Timestamp"; + public static final String CHANNEL_ID_THERMOSTAT_SETPOINTCHANGESOURCETIMESTAMP = "thermostat-setpointchangesourcetimestamp"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SETPOINTCHANGESOURCETIMESTAMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SETPOINTCHANGESOURCETIMESTAMP); + + public static final String CHANNEL_NAME_THERMOSTAT_OCCUPIEDSETBACK = "OccupiedSetback"; + public static final String CHANNEL_LABEL_THERMOSTAT_OCCUPIEDSETBACK = "Occupied Setback"; + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDSETBACK = "thermostat-occupiedsetback"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDSETBACK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OCCUPIEDSETBACK); + + public static final String CHANNEL_NAME_THERMOSTAT_OCCUPIEDSETBACKMIN = "OccupiedSetbackMin"; + public static final String CHANNEL_LABEL_THERMOSTAT_OCCUPIEDSETBACKMIN = "Occupied Setback Min"; + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDSETBACKMIN = "thermostat-occupiedsetbackmin"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDSETBACKMIN = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OCCUPIEDSETBACKMIN); + + public static final String CHANNEL_NAME_THERMOSTAT_OCCUPIEDSETBACKMAX = "OccupiedSetbackMax"; + public static final String CHANNEL_LABEL_THERMOSTAT_OCCUPIEDSETBACKMAX = "Occupied Setback Max"; + public static final String CHANNEL_ID_THERMOSTAT_OCCUPIEDSETBACKMAX = "thermostat-occupiedsetbackmax"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_OCCUPIEDSETBACKMAX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_OCCUPIEDSETBACKMAX); + + public static final String CHANNEL_NAME_THERMOSTAT_UNOCCUPIEDSETBACK = "UnoccupiedSetback"; + public static final String CHANNEL_LABEL_THERMOSTAT_UNOCCUPIEDSETBACK = "Unoccupied Setback"; + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDSETBACK = "thermostat-unoccupiedsetback"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDSETBACK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDSETBACK); + + public static final String CHANNEL_NAME_THERMOSTAT_UNOCCUPIEDSETBACKMIN = "UnoccupiedSetbackMin"; + public static final String CHANNEL_LABEL_THERMOSTAT_UNOCCUPIEDSETBACKMIN = "Unoccupied Setback Min"; + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDSETBACKMIN = "thermostat-unoccupiedsetbackmin"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDSETBACKMIN = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDSETBACKMIN); + + public static final String CHANNEL_NAME_THERMOSTAT_UNOCCUPIEDSETBACKMAX = "UnoccupiedSetbackMax"; + public static final String CHANNEL_LABEL_THERMOSTAT_UNOCCUPIEDSETBACKMAX = "Unoccupied Setback Max"; + public static final String CHANNEL_ID_THERMOSTAT_UNOCCUPIEDSETBACKMAX = "thermostat-unoccupiedsetbackmax"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_UNOCCUPIEDSETBACKMAX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_UNOCCUPIEDSETBACKMAX); + + public static final String CHANNEL_NAME_THERMOSTAT_EMERGENCYHEATDELTA = "EmergencyHeatDelta"; + public static final String CHANNEL_LABEL_THERMOSTAT_EMERGENCYHEATDELTA = "Emergency Heat Delta"; + public static final String CHANNEL_ID_THERMOSTAT_EMERGENCYHEATDELTA = "thermostat-emergencyheatdelta"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_EMERGENCYHEATDELTA = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_EMERGENCYHEATDELTA); + + public static final String CHANNEL_NAME_THERMOSTAT_ACTYPE = "AcType"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACTYPE = "Ac Type"; + public static final String CHANNEL_ID_THERMOSTAT_ACTYPE = "thermostat-actype"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACTYPE); + + public static final String CHANNEL_NAME_THERMOSTAT_ACCAPACITY = "AcCapacity"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACCAPACITY = "Ac Capacity"; + public static final String CHANNEL_ID_THERMOSTAT_ACCAPACITY = "thermostat-accapacity"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACCAPACITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACCAPACITY); + + public static final String CHANNEL_NAME_THERMOSTAT_ACREFRIGERANTTYPE = "AcRefrigerantType"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACREFRIGERANTTYPE = "Ac Refrigerant Type"; + public static final String CHANNEL_ID_THERMOSTAT_ACREFRIGERANTTYPE = "thermostat-acrefrigeranttype"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACREFRIGERANTTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACREFRIGERANTTYPE); + + public static final String CHANNEL_NAME_THERMOSTAT_ACCOMPRESSORTYPE = "AcCompressorType"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACCOMPRESSORTYPE = "Ac Compressor Type"; + public static final String CHANNEL_ID_THERMOSTAT_ACCOMPRESSORTYPE = "thermostat-accompressortype"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACCOMPRESSORTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACCOMPRESSORTYPE); + + public static final String CHANNEL_NAME_THERMOSTAT_ACERRORCODE = "AcErrorCode"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACERRORCODE = "Ac Error Code"; + public static final String CHANNEL_ID_THERMOSTAT_ACERRORCODE = "thermostat-acerrorcode"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACERRORCODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACERRORCODE); + + public static final String CHANNEL_NAME_THERMOSTAT_ACLOUVERPOSITION = "AcLouverPosition"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACLOUVERPOSITION = "Ac Louver Position"; + public static final String CHANNEL_ID_THERMOSTAT_ACLOUVERPOSITION = "thermostat-aclouverposition"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACLOUVERPOSITION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACLOUVERPOSITION); + + public static final String CHANNEL_NAME_THERMOSTAT_ACCOILTEMPERATURE = "AcCoilTemperature"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACCOILTEMPERATURE = "Ac Coil Temperature"; + public static final String CHANNEL_ID_THERMOSTAT_ACCOILTEMPERATURE = "thermostat-accoiltemperature"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACCOILTEMPERATURE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACCOILTEMPERATURE); + + public static final String CHANNEL_NAME_THERMOSTAT_ACCAPACITYFORMAT = "AcCapacityFormat"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACCAPACITYFORMAT = "Ac Capacity Format"; + public static final String CHANNEL_ID_THERMOSTAT_ACCAPACITYFORMAT = "thermostat-accapacityformat"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACCAPACITYFORMAT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACCAPACITYFORMAT); + + public static final String CHANNEL_NAME_THERMOSTAT_PRESETTYPES = "PresetTypes"; + public static final String CHANNEL_LABEL_THERMOSTAT_PRESETTYPES = "Preset Types"; + public static final String CHANNEL_ID_THERMOSTAT_PRESETTYPES = "thermostat-presettypes"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_PRESETTYPES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_PRESETTYPES); + + public static final String CHANNEL_NAME_THERMOSTAT_SCHEDULETYPES = "ScheduleTypes"; + public static final String CHANNEL_LABEL_THERMOSTAT_SCHEDULETYPES = "Schedule Types"; + public static final String CHANNEL_ID_THERMOSTAT_SCHEDULETYPES = "thermostat-scheduletypes"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SCHEDULETYPES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SCHEDULETYPES); + + public static final String CHANNEL_NAME_THERMOSTAT_NUMBEROFPRESETS = "NumberOfPresets"; + public static final String CHANNEL_LABEL_THERMOSTAT_NUMBEROFPRESETS = "Number Of Presets"; + public static final String CHANNEL_ID_THERMOSTAT_NUMBEROFPRESETS = "thermostat-numberofpresets"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_NUMBEROFPRESETS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_NUMBEROFPRESETS); + + public static final String CHANNEL_NAME_THERMOSTAT_NUMBEROFSCHEDULES = "NumberOfSchedules"; + public static final String CHANNEL_LABEL_THERMOSTAT_NUMBEROFSCHEDULES = "Number Of Schedules"; + public static final String CHANNEL_ID_THERMOSTAT_NUMBEROFSCHEDULES = "thermostat-numberofschedules"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_NUMBEROFSCHEDULES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_NUMBEROFSCHEDULES); + + public static final String CHANNEL_NAME_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONS = "NumberOfScheduleTransitions"; + public static final String CHANNEL_LABEL_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONS = "Number Of Schedule Transitions"; + public static final String CHANNEL_ID_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONS = "thermostat-numberofscheduletransitions"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONS); + + public static final String CHANNEL_NAME_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONPERDAY = "NumberOfScheduleTransitionPerDay"; + public static final String CHANNEL_LABEL_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONPERDAY = "Number Of Schedule Transition Per Day"; + public static final String CHANNEL_ID_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONPERDAY = "thermostat-numberofscheduletransitionperday"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONPERDAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_NUMBEROFSCHEDULETRANSITIONPERDAY); + + public static final String CHANNEL_NAME_THERMOSTAT_ACTIVEPRESETHANDLE = "ActivePresetHandle"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACTIVEPRESETHANDLE = "Active Preset Handle"; + public static final String CHANNEL_ID_THERMOSTAT_ACTIVEPRESETHANDLE = "thermostat-activepresethandle"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACTIVEPRESETHANDLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACTIVEPRESETHANDLE); + + public static final String CHANNEL_NAME_THERMOSTAT_ACTIVESCHEDULEHANDLE = "ActiveScheduleHandle"; + public static final String CHANNEL_LABEL_THERMOSTAT_ACTIVESCHEDULEHANDLE = "Active Schedule Handle"; + public static final String CHANNEL_ID_THERMOSTAT_ACTIVESCHEDULEHANDLE = "thermostat-activeschedulehandle"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_ACTIVESCHEDULEHANDLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_ACTIVESCHEDULEHANDLE); + + public static final String CHANNEL_NAME_THERMOSTAT_PRESETS = "Presets"; + public static final String CHANNEL_LABEL_THERMOSTAT_PRESETS = "Presets"; + public static final String CHANNEL_ID_THERMOSTAT_PRESETS = "thermostat-presets"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_PRESETS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_PRESETS); + + public static final String CHANNEL_NAME_THERMOSTAT_SCHEDULES = "Schedules"; + public static final String CHANNEL_LABEL_THERMOSTAT_SCHEDULES = "Schedules"; + public static final String CHANNEL_ID_THERMOSTAT_SCHEDULES = "thermostat-schedules"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SCHEDULES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SCHEDULES); + + public static final String CHANNEL_NAME_THERMOSTAT_SETPOINTHOLDEXPIRYTIMESTAMP = "SetpointHoldExpiryTimestamp"; + public static final String CHANNEL_LABEL_THERMOSTAT_SETPOINTHOLDEXPIRYTIMESTAMP = "Setpoint Hold Expiry Timestamp"; + public static final String CHANNEL_ID_THERMOSTAT_SETPOINTHOLDEXPIRYTIMESTAMP = "thermostat-setpointholdexpirytimestamp"; + public static final ChannelTypeUID CHANNEL_THERMOSTAT_SETPOINTHOLDEXPIRYTIMESTAMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTAT_SETPOINTHOLDEXPIRYTIMESTAMP); + + // ThermostatUserInterfaceConfiguration Cluster + public static final String CHANNEL_NAME_THERMOSTATUSERINTERFACECONFIGURATION_TEMPERATUREDISPLAYMODE = "TemperatureDisplayMode"; + public static final String CHANNEL_LABEL_THERMOSTATUSERINTERFACECONFIGURATION_TEMPERATUREDISPLAYMODE = "Temperature Display Mode"; + public static final String CHANNEL_ID_THERMOSTATUSERINTERFACECONFIGURATION_TEMPERATUREDISPLAYMODE = "thermostatuserinterfaceconfiguration-temperaturedisplaymode"; + public static final ChannelTypeUID CHANNEL_THERMOSTATUSERINTERFACECONFIGURATION_TEMPERATUREDISPLAYMODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTATUSERINTERFACECONFIGURATION_TEMPERATUREDISPLAYMODE); + + public static final String CHANNEL_NAME_THERMOSTATUSERINTERFACECONFIGURATION_KEYPADLOCKOUT = "KeypadLockout"; + public static final String CHANNEL_LABEL_THERMOSTATUSERINTERFACECONFIGURATION_KEYPADLOCKOUT = "Keypad Lockout"; + public static final String CHANNEL_ID_THERMOSTATUSERINTERFACECONFIGURATION_KEYPADLOCKOUT = "thermostatuserinterfaceconfiguration-keypadlockout"; + public static final ChannelTypeUID CHANNEL_THERMOSTATUSERINTERFACECONFIGURATION_KEYPADLOCKOUT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTATUSERINTERFACECONFIGURATION_KEYPADLOCKOUT); + + public static final String CHANNEL_NAME_THERMOSTATUSERINTERFACECONFIGURATION_SCHEDULEPROGRAMMINGVISIBILITY = "ScheduleProgrammingVisibility"; + public static final String CHANNEL_LABEL_THERMOSTATUSERINTERFACECONFIGURATION_SCHEDULEPROGRAMMINGVISIBILITY = "Schedule Programming Visibility"; + public static final String CHANNEL_ID_THERMOSTATUSERINTERFACECONFIGURATION_SCHEDULEPROGRAMMINGVISIBILITY = "thermostatuserinterfaceconfiguration-scheduleprogrammingvisibility"; + public static final ChannelTypeUID CHANNEL_THERMOSTATUSERINTERFACECONFIGURATION_SCHEDULEPROGRAMMINGVISIBILITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THERMOSTATUSERINTERFACECONFIGURATION_SCHEDULEPROGRAMMINGVISIBILITY); + + // ThreadBorderRouterManagement Cluster + public static final String CHANNEL_NAME_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME = "BorderRouterName"; + public static final String CHANNEL_LABEL_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME = "Border Router Name"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME = "threadborderroutermanagement-borderroutername"; + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERROUTERNAME); + + public static final String CHANNEL_NAME_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID = "BorderAgentId"; + public static final String CHANNEL_LABEL_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID = "Border Agent Id"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID = "threadborderroutermanagement-borderagentid"; + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_BORDERAGENTID); + + public static final String CHANNEL_NAME_THREADBORDERROUTERMANAGEMENT_THREADVERSION = "ThreadVersion"; + public static final String CHANNEL_LABEL_THREADBORDERROUTERMANAGEMENT_THREADVERSION = "Thread Version"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_THREADVERSION = "threadborderroutermanagement-threadversion"; + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_THREADVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_THREADVERSION); + + public static final String CHANNEL_NAME_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED = "InterfaceEnabled"; + public static final String CHANNEL_LABEL_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED = "Interface Enabled"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED = "threadborderroutermanagement-interfaceenabled"; + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_INTERFACEENABLED); + + public static final String CHANNEL_NAME_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP = "ActiveDatasetTimestamp"; + public static final String CHANNEL_LABEL_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP = "Active Dataset Timestamp"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP = "threadborderroutermanagement-activedatasettimestamp"; + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_ACTIVEDATASETTIMESTAMP); + + public static final String CHANNEL_NAME_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP = "PendingDatasetTimestamp"; + public static final String CHANNEL_LABEL_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP = "Pending Dataset Timestamp"; + public static final String CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP = "threadborderroutermanagement-pendingdatasettimestamp"; + public static final ChannelTypeUID CHANNEL_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADBORDERROUTERMANAGEMENT_PENDINGDATASETTIMESTAMP); + + // ThreadNetworkDiagnostics Cluster + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_CHANNEL = "Channel"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_CHANNEL = "Channel"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_CHANNEL = "threadnetworkdiagnostics-channel"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_CHANNEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_CHANNEL); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_ROUTINGROLE = "RoutingRole"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_ROUTINGROLE = "Routing Role"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ROUTINGROLE = "threadnetworkdiagnostics-routingrole"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_ROUTINGROLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ROUTINGROLE); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_NETWORKNAME = "NetworkName"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_NETWORKNAME = "Network Name"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_NETWORKNAME = "threadnetworkdiagnostics-networkname"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_NETWORKNAME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_NETWORKNAME); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_PANID = "PanId"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_PANID = "Pan Id"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PANID = "threadnetworkdiagnostics-panid"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_PANID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PANID); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_EXTENDEDPANID = "ExtendedPanId"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_EXTENDEDPANID = "Extended Pan Id"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_EXTENDEDPANID = "threadnetworkdiagnostics-extendedpanid"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_EXTENDEDPANID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_EXTENDEDPANID); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_MESHLOCALPREFIX = "MeshLocalPrefix"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_MESHLOCALPREFIX = "Mesh Local Prefix"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_MESHLOCALPREFIX = "threadnetworkdiagnostics-meshlocalprefix"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_MESHLOCALPREFIX = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_MESHLOCALPREFIX); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_OVERRUNCOUNT = "OverrunCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_OVERRUNCOUNT = "Overrun Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_OVERRUNCOUNT = "threadnetworkdiagnostics-overruncount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_OVERRUNCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_OVERRUNCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_NEIGHBORTABLE = "NeighborTable"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_NEIGHBORTABLE = "Neighbor Table"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_NEIGHBORTABLE = "threadnetworkdiagnostics-neighbortable"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_NEIGHBORTABLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_NEIGHBORTABLE); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_ROUTETABLE = "RouteTable"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_ROUTETABLE = "Route Table"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ROUTETABLE = "threadnetworkdiagnostics-routetable"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_ROUTETABLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ROUTETABLE); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_PARTITIONID = "PartitionId"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_PARTITIONID = "Partition Id"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PARTITIONID = "threadnetworkdiagnostics-partitionid"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_PARTITIONID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PARTITIONID); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_WEIGHTING = "Weighting"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_WEIGHTING = "Weighting"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_WEIGHTING = "threadnetworkdiagnostics-weighting"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_WEIGHTING = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_WEIGHTING); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_DATAVERSION = "DataVersion"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_DATAVERSION = "Data Version"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_DATAVERSION = "threadnetworkdiagnostics-dataversion"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_DATAVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_DATAVERSION); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_STABLEDATAVERSION = "StableDataVersion"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_STABLEDATAVERSION = "Stable Data Version"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_STABLEDATAVERSION = "threadnetworkdiagnostics-stabledataversion"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_STABLEDATAVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_STABLEDATAVERSION); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_LEADERROUTERID = "LeaderRouterId"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_LEADERROUTERID = "Leader Router Id"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_LEADERROUTERID = "threadnetworkdiagnostics-leaderrouterid"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_LEADERROUTERID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_LEADERROUTERID); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_DETACHEDROLECOUNT = "DetachedRoleCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_DETACHEDROLECOUNT = "Detached Role Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_DETACHEDROLECOUNT = "threadnetworkdiagnostics-detachedrolecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_DETACHEDROLECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_DETACHEDROLECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_CHILDROLECOUNT = "ChildRoleCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_CHILDROLECOUNT = "Child Role Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_CHILDROLECOUNT = "threadnetworkdiagnostics-childrolecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_CHILDROLECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_CHILDROLECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_ROUTERROLECOUNT = "RouterRoleCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_ROUTERROLECOUNT = "Router Role Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ROUTERROLECOUNT = "threadnetworkdiagnostics-routerrolecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_ROUTERROLECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ROUTERROLECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_LEADERROLECOUNT = "LeaderRoleCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_LEADERROLECOUNT = "Leader Role Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_LEADERROLECOUNT = "threadnetworkdiagnostics-leaderrolecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_LEADERROLECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_LEADERROLECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_ATTACHATTEMPTCOUNT = "AttachAttemptCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_ATTACHATTEMPTCOUNT = "Attach Attempt Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ATTACHATTEMPTCOUNT = "threadnetworkdiagnostics-attachattemptcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_ATTACHATTEMPTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ATTACHATTEMPTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_PARTITIONIDCHANGECOUNT = "PartitionIdChangeCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_PARTITIONIDCHANGECOUNT = "Partition Id Change Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PARTITIONIDCHANGECOUNT = "threadnetworkdiagnostics-partitionidchangecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_PARTITIONIDCHANGECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PARTITIONIDCHANGECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_BETTERPARTITIONATTACHATTEMPTCOUNT = "BetterPartitionAttachAttemptCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_BETTERPARTITIONATTACHATTEMPTCOUNT = "Better Partition Attach Attempt Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_BETTERPARTITIONATTACHATTEMPTCOUNT = "threadnetworkdiagnostics-betterpartitionattachattemptcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_BETTERPARTITIONATTACHATTEMPTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_BETTERPARTITIONATTACHATTEMPTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_PARENTCHANGECOUNT = "ParentChangeCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_PARENTCHANGECOUNT = "Parent Change Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PARENTCHANGECOUNT = "threadnetworkdiagnostics-parentchangecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_PARENTCHANGECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PARENTCHANGECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXTOTALCOUNT = "TxTotalCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXTOTALCOUNT = "Tx Total Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXTOTALCOUNT = "threadnetworkdiagnostics-txtotalcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXTOTALCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXTOTALCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXUNICASTCOUNT = "TxUnicastCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXUNICASTCOUNT = "Tx Unicast Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXUNICASTCOUNT = "threadnetworkdiagnostics-txunicastcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXUNICASTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXUNICASTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXBROADCASTCOUNT = "TxBroadcastCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXBROADCASTCOUNT = "Tx Broadcast Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXBROADCASTCOUNT = "threadnetworkdiagnostics-txbroadcastcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXBROADCASTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXBROADCASTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXACKREQUESTEDCOUNT = "TxAckRequestedCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXACKREQUESTEDCOUNT = "Tx Ack Requested Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXACKREQUESTEDCOUNT = "threadnetworkdiagnostics-txackrequestedcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXACKREQUESTEDCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXACKREQUESTEDCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXACKEDCOUNT = "TxAckedCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXACKEDCOUNT = "Tx Acked Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXACKEDCOUNT = "threadnetworkdiagnostics-txackedcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXACKEDCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXACKEDCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXNOACKREQUESTEDCOUNT = "TxNoAckRequestedCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXNOACKREQUESTEDCOUNT = "Tx No Ack Requested Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXNOACKREQUESTEDCOUNT = "threadnetworkdiagnostics-txnoackrequestedcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXNOACKREQUESTEDCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXNOACKREQUESTEDCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXDATACOUNT = "TxDataCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXDATACOUNT = "Tx Data Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXDATACOUNT = "threadnetworkdiagnostics-txdatacount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXDATACOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXDATACOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXDATAPOLLCOUNT = "TxDataPollCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXDATAPOLLCOUNT = "Tx Data Poll Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXDATAPOLLCOUNT = "threadnetworkdiagnostics-txdatapollcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXDATAPOLLCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXDATAPOLLCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXBEACONCOUNT = "TxBeaconCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXBEACONCOUNT = "Tx Beacon Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXBEACONCOUNT = "threadnetworkdiagnostics-txbeaconcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXBEACONCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXBEACONCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXBEACONREQUESTCOUNT = "TxBeaconRequestCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXBEACONREQUESTCOUNT = "Tx Beacon Request Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXBEACONREQUESTCOUNT = "threadnetworkdiagnostics-txbeaconrequestcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXBEACONREQUESTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXBEACONREQUESTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXOTHERCOUNT = "TxOtherCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXOTHERCOUNT = "Tx Other Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXOTHERCOUNT = "threadnetworkdiagnostics-txothercount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXOTHERCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXOTHERCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXRETRYCOUNT = "TxRetryCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXRETRYCOUNT = "Tx Retry Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXRETRYCOUNT = "threadnetworkdiagnostics-txretrycount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXRETRYCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXRETRYCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXDIRECTMAXRETRYEXPIRYCOUNT = "TxDirectMaxRetryExpiryCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXDIRECTMAXRETRYEXPIRYCOUNT = "Tx Direct Max Retry Expiry Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXDIRECTMAXRETRYEXPIRYCOUNT = "threadnetworkdiagnostics-txdirectmaxretryexpirycount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXDIRECTMAXRETRYEXPIRYCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXDIRECTMAXRETRYEXPIRYCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXINDIRECTMAXRETRYEXPIRYCOUNT = "TxIndirectMaxRetryExpiryCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXINDIRECTMAXRETRYEXPIRYCOUNT = "Tx Indirect Max Retry Expiry Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXINDIRECTMAXRETRYEXPIRYCOUNT = "threadnetworkdiagnostics-txindirectmaxretryexpirycount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXINDIRECTMAXRETRYEXPIRYCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXINDIRECTMAXRETRYEXPIRYCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXERRCCACOUNT = "TxErrCcaCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXERRCCACOUNT = "Tx Err Cca Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXERRCCACOUNT = "threadnetworkdiagnostics-txerrccacount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXERRCCACOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXERRCCACOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXERRABORTCOUNT = "TxErrAbortCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXERRABORTCOUNT = "Tx Err Abort Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXERRABORTCOUNT = "threadnetworkdiagnostics-txerrabortcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXERRABORTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXERRABORTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_TXERRBUSYCHANNELCOUNT = "TxErrBusyChannelCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_TXERRBUSYCHANNELCOUNT = "Tx Err Busy Channel Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXERRBUSYCHANNELCOUNT = "threadnetworkdiagnostics-txerrbusychannelcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_TXERRBUSYCHANNELCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_TXERRBUSYCHANNELCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXTOTALCOUNT = "RxTotalCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXTOTALCOUNT = "Rx Total Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXTOTALCOUNT = "threadnetworkdiagnostics-rxtotalcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXTOTALCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXTOTALCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXUNICASTCOUNT = "RxUnicastCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXUNICASTCOUNT = "Rx Unicast Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXUNICASTCOUNT = "threadnetworkdiagnostics-rxunicastcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXUNICASTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXUNICASTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXBROADCASTCOUNT = "RxBroadcastCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXBROADCASTCOUNT = "Rx Broadcast Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXBROADCASTCOUNT = "threadnetworkdiagnostics-rxbroadcastcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXBROADCASTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXBROADCASTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXDATACOUNT = "RxDataCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXDATACOUNT = "Rx Data Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDATACOUNT = "threadnetworkdiagnostics-rxdatacount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXDATACOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDATACOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXDATAPOLLCOUNT = "RxDataPollCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXDATAPOLLCOUNT = "Rx Data Poll Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDATAPOLLCOUNT = "threadnetworkdiagnostics-rxdatapollcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXDATAPOLLCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDATAPOLLCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXBEACONCOUNT = "RxBeaconCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXBEACONCOUNT = "Rx Beacon Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXBEACONCOUNT = "threadnetworkdiagnostics-rxbeaconcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXBEACONCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXBEACONCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXBEACONREQUESTCOUNT = "RxBeaconRequestCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXBEACONREQUESTCOUNT = "Rx Beacon Request Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXBEACONREQUESTCOUNT = "threadnetworkdiagnostics-rxbeaconrequestcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXBEACONREQUESTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXBEACONREQUESTCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXOTHERCOUNT = "RxOtherCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXOTHERCOUNT = "Rx Other Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXOTHERCOUNT = "threadnetworkdiagnostics-rxothercount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXOTHERCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXOTHERCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXADDRESSFILTEREDCOUNT = "RxAddressFilteredCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXADDRESSFILTEREDCOUNT = "Rx Address Filtered Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXADDRESSFILTEREDCOUNT = "threadnetworkdiagnostics-rxaddressfilteredcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXADDRESSFILTEREDCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXADDRESSFILTEREDCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXDESTADDRFILTEREDCOUNT = "RxDestAddrFilteredCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXDESTADDRFILTEREDCOUNT = "Rx Dest Addr Filtered Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDESTADDRFILTEREDCOUNT = "threadnetworkdiagnostics-rxdestaddrfilteredcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXDESTADDRFILTEREDCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDESTADDRFILTEREDCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXDUPLICATEDCOUNT = "RxDuplicatedCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXDUPLICATEDCOUNT = "Rx Duplicated Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDUPLICATEDCOUNT = "threadnetworkdiagnostics-rxduplicatedcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXDUPLICATEDCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXDUPLICATEDCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXERRNOFRAMECOUNT = "RxErrNoFrameCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXERRNOFRAMECOUNT = "Rx Err No Frame Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRNOFRAMECOUNT = "threadnetworkdiagnostics-rxerrnoframecount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXERRNOFRAMECOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRNOFRAMECOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXERRUNKNOWNNEIGHBORCOUNT = "RxErrUnknownNeighborCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXERRUNKNOWNNEIGHBORCOUNT = "Rx Err Unknown Neighbor Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRUNKNOWNNEIGHBORCOUNT = "threadnetworkdiagnostics-rxerrunknownneighborcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXERRUNKNOWNNEIGHBORCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRUNKNOWNNEIGHBORCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXERRINVALIDSRCADDRCOUNT = "RxErrInvalidSrcAddrCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXERRINVALIDSRCADDRCOUNT = "Rx Err Invalid Src Addr Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRINVALIDSRCADDRCOUNT = "threadnetworkdiagnostics-rxerrinvalidsrcaddrcount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXERRINVALIDSRCADDRCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRINVALIDSRCADDRCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXERRSECCOUNT = "RxErrSecCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXERRSECCOUNT = "Rx Err Sec Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRSECCOUNT = "threadnetworkdiagnostics-rxerrseccount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXERRSECCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRSECCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXERRFCSCOUNT = "RxErrFcsCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXERRFCSCOUNT = "Rx Err Fcs Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRFCSCOUNT = "threadnetworkdiagnostics-rxerrfcscount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXERRFCSCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERRFCSCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RXERROTHERCOUNT = "RxErrOtherCount"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RXERROTHERCOUNT = "Rx Err Other Count"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERROTHERCOUNT = "threadnetworkdiagnostics-rxerrothercount"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RXERROTHERCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RXERROTHERCOUNT); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_ACTIVETIMESTAMP = "ActiveTimestamp"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_ACTIVETIMESTAMP = "Active Timestamp"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ACTIVETIMESTAMP = "threadnetworkdiagnostics-activetimestamp"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_ACTIVETIMESTAMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ACTIVETIMESTAMP); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_PENDINGTIMESTAMP = "PendingTimestamp"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_PENDINGTIMESTAMP = "Pending Timestamp"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PENDINGTIMESTAMP = "threadnetworkdiagnostics-pendingtimestamp"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_PENDINGTIMESTAMP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_PENDINGTIMESTAMP); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_DELAY = "Delay"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_DELAY = "Delay"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_DELAY = "threadnetworkdiagnostics-delay"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_DELAY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_DELAY); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_SECURITYPOLICY = "SecurityPolicy"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_SECURITYPOLICY = "Security Policy"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_SECURITYPOLICY = "threadnetworkdiagnostics-securitypolicy"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_SECURITYPOLICY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_SECURITYPOLICY); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_CHANNELPAGE0MASK = "ChannelPage0Mask"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_CHANNELPAGE0MASK = "Channel Page0mask"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_CHANNELPAGE0MASK = "threadnetworkdiagnostics-channelpage0mask"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_CHANNELPAGE0MASK = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_CHANNELPAGE0MASK); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_OPERATIONALDATASETCOMPONENTS = "OperationalDatasetComponents"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_OPERATIONALDATASETCOMPONENTS = "Operational Dataset Components"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_OPERATIONALDATASETCOMPONENTS = "threadnetworkdiagnostics-operationaldatasetcomponents"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_OPERATIONALDATASETCOMPONENTS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_OPERATIONALDATASETCOMPONENTS); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_ACTIVENETWORKFAULTSLIST = "ActiveNetworkFaultsList"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_ACTIVENETWORKFAULTSLIST = "Active Network Faults List"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ACTIVENETWORKFAULTSLIST = "threadnetworkdiagnostics-activenetworkfaultslist"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_ACTIVENETWORKFAULTSLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_ACTIVENETWORKFAULTSLIST); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_EXTADDRESS = "ExtAddress"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_EXTADDRESS = "Ext Address"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_EXTADDRESS = "threadnetworkdiagnostics-extaddress"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_EXTADDRESS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_EXTADDRESS); + + public static final String CHANNEL_NAME_THREADNETWORKDIAGNOSTICS_RLOC16 = "Rloc16"; + public static final String CHANNEL_LABEL_THREADNETWORKDIAGNOSTICS_RLOC16 = "Rloc16"; + public static final String CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RLOC16 = "threadnetworkdiagnostics-rloc16"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIAGNOSTICS_RLOC16 = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIAGNOSTICS_RLOC16); + + // ThreadNetworkDirectory Cluster + public static final String CHANNEL_NAME_THREADNETWORKDIRECTORY_PREFERREDEXTENDEDPANID = "PreferredExtendedPanId"; + public static final String CHANNEL_LABEL_THREADNETWORKDIRECTORY_PREFERREDEXTENDEDPANID = "Preferred Extended Pan Id"; + public static final String CHANNEL_ID_THREADNETWORKDIRECTORY_PREFERREDEXTENDEDPANID = "threadnetworkdirectory-preferredextendedpanid"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIRECTORY_PREFERREDEXTENDEDPANID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIRECTORY_PREFERREDEXTENDEDPANID); + + public static final String CHANNEL_NAME_THREADNETWORKDIRECTORY_THREADNETWORKS = "ThreadNetworks"; + public static final String CHANNEL_LABEL_THREADNETWORKDIRECTORY_THREADNETWORKS = "Thread Networks"; + public static final String CHANNEL_ID_THREADNETWORKDIRECTORY_THREADNETWORKS = "threadnetworkdirectory-threadnetworks"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIRECTORY_THREADNETWORKS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIRECTORY_THREADNETWORKS); + + public static final String CHANNEL_NAME_THREADNETWORKDIRECTORY_THREADNETWORKTABLESIZE = "ThreadNetworkTableSize"; + public static final String CHANNEL_LABEL_THREADNETWORKDIRECTORY_THREADNETWORKTABLESIZE = "Thread Network Table Size"; + public static final String CHANNEL_ID_THREADNETWORKDIRECTORY_THREADNETWORKTABLESIZE = "threadnetworkdirectory-threadnetworktablesize"; + public static final ChannelTypeUID CHANNEL_THREADNETWORKDIRECTORY_THREADNETWORKTABLESIZE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_THREADNETWORKDIRECTORY_THREADNETWORKTABLESIZE); + + // TimeFormatLocalization Cluster + public static final String CHANNEL_NAME_TIMEFORMATLOCALIZATION_HOURFORMAT = "HourFormat"; + public static final String CHANNEL_LABEL_TIMEFORMATLOCALIZATION_HOURFORMAT = "Hour Format"; + public static final String CHANNEL_ID_TIMEFORMATLOCALIZATION_HOURFORMAT = "timeformatlocalization-hourformat"; + public static final ChannelTypeUID CHANNEL_TIMEFORMATLOCALIZATION_HOURFORMAT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMEFORMATLOCALIZATION_HOURFORMAT); + + public static final String CHANNEL_NAME_TIMEFORMATLOCALIZATION_ACTIVECALENDARTYPE = "ActiveCalendarType"; + public static final String CHANNEL_LABEL_TIMEFORMATLOCALIZATION_ACTIVECALENDARTYPE = "Active Calendar Type"; + public static final String CHANNEL_ID_TIMEFORMATLOCALIZATION_ACTIVECALENDARTYPE = "timeformatlocalization-activecalendartype"; + public static final ChannelTypeUID CHANNEL_TIMEFORMATLOCALIZATION_ACTIVECALENDARTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMEFORMATLOCALIZATION_ACTIVECALENDARTYPE); + + public static final String CHANNEL_NAME_TIMEFORMATLOCALIZATION_SUPPORTEDCALENDARTYPES = "SupportedCalendarTypes"; + public static final String CHANNEL_LABEL_TIMEFORMATLOCALIZATION_SUPPORTEDCALENDARTYPES = "Supported Calendar Types"; + public static final String CHANNEL_ID_TIMEFORMATLOCALIZATION_SUPPORTEDCALENDARTYPES = "timeformatlocalization-supportedcalendartypes"; + public static final ChannelTypeUID CHANNEL_TIMEFORMATLOCALIZATION_SUPPORTEDCALENDARTYPES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMEFORMATLOCALIZATION_SUPPORTEDCALENDARTYPES); + + // TimeSynchronization Cluster + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_UTCTIME = "UtcTime"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_UTCTIME = "Utc Time"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_UTCTIME = "timesynchronization-utctime"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_UTCTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_UTCTIME); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_GRANULARITY = "Granularity"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_GRANULARITY = "Granularity"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_GRANULARITY = "timesynchronization-granularity"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_GRANULARITY = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_GRANULARITY); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_TIMESOURCE = "TimeSource"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_TIMESOURCE = "Time Source"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_TIMESOURCE = "timesynchronization-timesource"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_TIMESOURCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_TIMESOURCE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_TRUSTEDTIMESOURCE = "TrustedTimeSource"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_TRUSTEDTIMESOURCE = "Trusted Time Source"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_TRUSTEDTIMESOURCE = "timesynchronization-trustedtimesource"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_TRUSTEDTIMESOURCE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_TRUSTEDTIMESOURCE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_DEFAULTNTP = "DefaultNtp"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_DEFAULTNTP = "Default Ntp"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_DEFAULTNTP = "timesynchronization-defaultntp"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_DEFAULTNTP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_DEFAULTNTP); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_TIMEZONE = "TimeZone"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_TIMEZONE = "Time Zone"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_TIMEZONE = "timesynchronization-timezone"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_TIMEZONE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_TIMEZONE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_DSTOFFSET = "DstOffset"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_DSTOFFSET = "Dst Offset"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_DSTOFFSET = "timesynchronization-dstoffset"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_DSTOFFSET = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_DSTOFFSET); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_LOCALTIME = "LocalTime"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_LOCALTIME = "Local Time"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_LOCALTIME = "timesynchronization-localtime"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_LOCALTIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_LOCALTIME); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_TIMEZONEDATABASE = "TimeZoneDatabase"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_TIMEZONEDATABASE = "Time Zone Database"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_TIMEZONEDATABASE = "timesynchronization-timezonedatabase"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_TIMEZONEDATABASE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_TIMEZONEDATABASE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_NTPSERVERAVAILABLE = "NtpServerAvailable"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_NTPSERVERAVAILABLE = "Ntp Server Available"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_NTPSERVERAVAILABLE = "timesynchronization-ntpserveravailable"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_NTPSERVERAVAILABLE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_NTPSERVERAVAILABLE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_TIMEZONELISTMAXSIZE = "TimeZoneListMaxSize"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_TIMEZONELISTMAXSIZE = "Time Zone List Max Size"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_TIMEZONELISTMAXSIZE = "timesynchronization-timezonelistmaxsize"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_TIMEZONELISTMAXSIZE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_TIMEZONELISTMAXSIZE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_DSTOFFSETLISTMAXSIZE = "DstOffsetListMaxSize"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_DSTOFFSETLISTMAXSIZE = "Dst Offset List Max Size"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_DSTOFFSETLISTMAXSIZE = "timesynchronization-dstoffsetlistmaxsize"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_DSTOFFSETLISTMAXSIZE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_DSTOFFSETLISTMAXSIZE); + + public static final String CHANNEL_NAME_TIMESYNCHRONIZATION_SUPPORTSDNSRESOLVE = "SupportsDnsResolve"; + public static final String CHANNEL_LABEL_TIMESYNCHRONIZATION_SUPPORTSDNSRESOLVE = "Supports Dns Resolve"; + public static final String CHANNEL_ID_TIMESYNCHRONIZATION_SUPPORTSDNSRESOLVE = "timesynchronization-supportsdnsresolve"; + public static final ChannelTypeUID CHANNEL_TIMESYNCHRONIZATION_SUPPORTSDNSRESOLVE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_TIMESYNCHRONIZATION_SUPPORTSDNSRESOLVE); + + // TotalVolatileOrganicCompoundsConcentrationMeasurement Cluster + // UnitLocalization Cluster + public static final String CHANNEL_NAME_UNITLOCALIZATION_TEMPERATUREUNIT = "TemperatureUnit"; + public static final String CHANNEL_LABEL_UNITLOCALIZATION_TEMPERATUREUNIT = "Temperature Unit"; + public static final String CHANNEL_ID_UNITLOCALIZATION_TEMPERATUREUNIT = "unitlocalization-temperatureunit"; + public static final ChannelTypeUID CHANNEL_UNITLOCALIZATION_TEMPERATUREUNIT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_UNITLOCALIZATION_TEMPERATUREUNIT); + + // UserLabel Cluster + public static final String CHANNEL_NAME_USERLABEL_LABELLIST = "LabelList"; + public static final String CHANNEL_LABEL_USERLABEL_LABELLIST = "Label List"; + public static final String CHANNEL_ID_USERLABEL_LABELLIST = "userlabel-labellist"; + public static final ChannelTypeUID CHANNEL_USERLABEL_LABELLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_USERLABEL_LABELLIST); + + // ValidProxies Cluster + public static final String CHANNEL_NAME_VALIDPROXIES_VALIDPROXYLIST = "ValidProxyList"; + public static final String CHANNEL_LABEL_VALIDPROXIES_VALIDPROXYLIST = "Valid Proxy List"; + public static final String CHANNEL_ID_VALIDPROXIES_VALIDPROXYLIST = "validproxies-validproxylist"; + public static final ChannelTypeUID CHANNEL_VALIDPROXIES_VALIDPROXYLIST = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALIDPROXIES_VALIDPROXYLIST); + + // ValveConfigurationAndControl Cluster + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_OPENDURATION = "OpenDuration"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_OPENDURATION = "Open Duration"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_OPENDURATION = "valveconfigurationandcontrol-openduration"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_OPENDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_OPENDURATION); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENDURATION = "DefaultOpenDuration"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENDURATION = "Default Open Duration"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENDURATION = "valveconfigurationandcontrol-defaultopenduration"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENDURATION); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_AUTOCLOSETIME = "AutoCloseTime"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_AUTOCLOSETIME = "Auto Close Time"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_AUTOCLOSETIME = "valveconfigurationandcontrol-autoclosetime"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_AUTOCLOSETIME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_AUTOCLOSETIME); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_REMAININGDURATION = "RemainingDuration"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_REMAININGDURATION = "Remaining Duration"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_REMAININGDURATION = "valveconfigurationandcontrol-remainingduration"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_REMAININGDURATION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_REMAININGDURATION); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_CURRENTSTATE = "CurrentState"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_CURRENTSTATE = "Current State"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_CURRENTSTATE = "valveconfigurationandcontrol-currentstate"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_CURRENTSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_CURRENTSTATE); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_TARGETSTATE = "TargetState"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_TARGETSTATE = "Target State"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_TARGETSTATE = "valveconfigurationandcontrol-targetstate"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_TARGETSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_TARGETSTATE); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_CURRENTLEVEL = "CurrentLevel"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_CURRENTLEVEL = "Current Level"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_CURRENTLEVEL = "valveconfigurationandcontrol-currentlevel"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_CURRENTLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_CURRENTLEVEL); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_TARGETLEVEL = "TargetLevel"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_TARGETLEVEL = "Target Level"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_TARGETLEVEL = "valveconfigurationandcontrol-targetlevel"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_TARGETLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_TARGETLEVEL); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENLEVEL = "DefaultOpenLevel"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENLEVEL = "Default Open Level"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENLEVEL = "valveconfigurationandcontrol-defaultopenlevel"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENLEVEL = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_DEFAULTOPENLEVEL); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_VALVEFAULT = "ValveFault"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_VALVEFAULT = "Valve Fault"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_VALVEFAULT = "valveconfigurationandcontrol-valvefault"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_VALVEFAULT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_VALVEFAULT); + + public static final String CHANNEL_NAME_VALVECONFIGURATIONANDCONTROL_LEVELSTEP = "LevelStep"; + public static final String CHANNEL_LABEL_VALVECONFIGURATIONANDCONTROL_LEVELSTEP = "Level Step"; + public static final String CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_LEVELSTEP = "valveconfigurationandcontrol-levelstep"; + public static final ChannelTypeUID CHANNEL_VALVECONFIGURATIONANDCONTROL_LEVELSTEP = new ChannelTypeUID( + "matter:" + CHANNEL_ID_VALVECONFIGURATIONANDCONTROL_LEVELSTEP); + + // WakeOnLan Cluster + public static final String CHANNEL_NAME_WAKEONLAN_MACADDRESS = "MacAddress"; + public static final String CHANNEL_LABEL_WAKEONLAN_MACADDRESS = "Mac Address"; + public static final String CHANNEL_ID_WAKEONLAN_MACADDRESS = "wakeonlan-macaddress"; + public static final ChannelTypeUID CHANNEL_WAKEONLAN_MACADDRESS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WAKEONLAN_MACADDRESS); + + public static final String CHANNEL_NAME_WAKEONLAN_LINKLOCALADDRESS = "LinkLocalAddress"; + public static final String CHANNEL_LABEL_WAKEONLAN_LINKLOCALADDRESS = "Link Local Address"; + public static final String CHANNEL_ID_WAKEONLAN_LINKLOCALADDRESS = "wakeonlan-linklocaladdress"; + public static final ChannelTypeUID CHANNEL_WAKEONLAN_LINKLOCALADDRESS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WAKEONLAN_LINKLOCALADDRESS); + + // WaterHeaterManagement Cluster + public static final String CHANNEL_NAME_WATERHEATERMANAGEMENT_HEATERTYPES = "HeaterTypes"; + public static final String CHANNEL_LABEL_WATERHEATERMANAGEMENT_HEATERTYPES = "Heater Types"; + public static final String CHANNEL_ID_WATERHEATERMANAGEMENT_HEATERTYPES = "waterheatermanagement-heatertypes"; + public static final ChannelTypeUID CHANNEL_WATERHEATERMANAGEMENT_HEATERTYPES = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WATERHEATERMANAGEMENT_HEATERTYPES); + + public static final String CHANNEL_NAME_WATERHEATERMANAGEMENT_HEATDEMAND = "HeatDemand"; + public static final String CHANNEL_LABEL_WATERHEATERMANAGEMENT_HEATDEMAND = "Heat Demand"; + public static final String CHANNEL_ID_WATERHEATERMANAGEMENT_HEATDEMAND = "waterheatermanagement-heatdemand"; + public static final ChannelTypeUID CHANNEL_WATERHEATERMANAGEMENT_HEATDEMAND = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WATERHEATERMANAGEMENT_HEATDEMAND); + + public static final String CHANNEL_NAME_WATERHEATERMANAGEMENT_TANKVOLUME = "TankVolume"; + public static final String CHANNEL_LABEL_WATERHEATERMANAGEMENT_TANKVOLUME = "Tank Volume"; + public static final String CHANNEL_ID_WATERHEATERMANAGEMENT_TANKVOLUME = "waterheatermanagement-tankvolume"; + public static final ChannelTypeUID CHANNEL_WATERHEATERMANAGEMENT_TANKVOLUME = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WATERHEATERMANAGEMENT_TANKVOLUME); + + public static final String CHANNEL_NAME_WATERHEATERMANAGEMENT_ESTIMATEDHEATREQUIRED = "EstimatedHeatRequired"; + public static final String CHANNEL_LABEL_WATERHEATERMANAGEMENT_ESTIMATEDHEATREQUIRED = "Estimated Heat Required"; + public static final String CHANNEL_ID_WATERHEATERMANAGEMENT_ESTIMATEDHEATREQUIRED = "waterheatermanagement-estimatedheatrequired"; + public static final ChannelTypeUID CHANNEL_WATERHEATERMANAGEMENT_ESTIMATEDHEATREQUIRED = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WATERHEATERMANAGEMENT_ESTIMATEDHEATREQUIRED); + + public static final String CHANNEL_NAME_WATERHEATERMANAGEMENT_TANKPERCENTAGE = "TankPercentage"; + public static final String CHANNEL_LABEL_WATERHEATERMANAGEMENT_TANKPERCENTAGE = "Tank Percentage"; + public static final String CHANNEL_ID_WATERHEATERMANAGEMENT_TANKPERCENTAGE = "waterheatermanagement-tankpercentage"; + public static final ChannelTypeUID CHANNEL_WATERHEATERMANAGEMENT_TANKPERCENTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WATERHEATERMANAGEMENT_TANKPERCENTAGE); + + public static final String CHANNEL_NAME_WATERHEATERMANAGEMENT_BOOSTSTATE = "BoostState"; + public static final String CHANNEL_LABEL_WATERHEATERMANAGEMENT_BOOSTSTATE = "Boost State"; + public static final String CHANNEL_ID_WATERHEATERMANAGEMENT_BOOSTSTATE = "waterheatermanagement-booststate"; + public static final ChannelTypeUID CHANNEL_WATERHEATERMANAGEMENT_BOOSTSTATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WATERHEATERMANAGEMENT_BOOSTSTATE); + + // WaterHeaterMode Cluster + // WaterTankLevelMonitoring Cluster + // WiFiNetworkDiagnostics Cluster + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_BSSID = "Bssid"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_BSSID = "Bssid"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_BSSID = "wifinetworkdiagnostics-bssid"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_BSSID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_BSSID); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_SECURITYTYPE = "SecurityType"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_SECURITYTYPE = "Security Type"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_SECURITYTYPE = "wifinetworkdiagnostics-securitytype"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_SECURITYTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_SECURITYTYPE); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_WIFIVERSION = "WiFiVersion"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_WIFIVERSION = "Wi Fi Version"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_WIFIVERSION = "wifinetworkdiagnostics-wifiversion"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_WIFIVERSION = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_WIFIVERSION); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_CHANNELNUMBER = "ChannelNumber"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_CHANNELNUMBER = "Channel Number"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_CHANNELNUMBER = "wifinetworkdiagnostics-channelnumber"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_CHANNELNUMBER = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_CHANNELNUMBER); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_RSSI = "Rssi"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_RSSI = "Rssi"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI = "wifinetworkdiagnostics-rssi"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_RSSI = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_BEACONLOSTCOUNT = "BeaconLostCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_BEACONLOSTCOUNT = "Beacon Lost Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_BEACONLOSTCOUNT = "wifinetworkdiagnostics-beaconlostcount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_BEACONLOSTCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_BEACONLOSTCOUNT); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_BEACONRXCOUNT = "BeaconRxCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_BEACONRXCOUNT = "Beacon Rx Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_BEACONRXCOUNT = "wifinetworkdiagnostics-beaconrxcount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_BEACONRXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_BEACONRXCOUNT); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTRXCOUNT = "PacketMulticastRxCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTRXCOUNT = "Packet Multicast Rx Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTRXCOUNT = "wifinetworkdiagnostics-packetmulticastrxcount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTRXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTRXCOUNT); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTTXCOUNT = "PacketMulticastTxCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTTXCOUNT = "Packet Multicast Tx Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTTXCOUNT = "wifinetworkdiagnostics-packetmulticasttxcount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTTXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETMULTICASTTXCOUNT); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_PACKETUNICASTRXCOUNT = "PacketUnicastRxCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_PACKETUNICASTRXCOUNT = "Packet Unicast Rx Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETUNICASTRXCOUNT = "wifinetworkdiagnostics-packetunicastrxcount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_PACKETUNICASTRXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETUNICASTRXCOUNT); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_PACKETUNICASTTXCOUNT = "PacketUnicastTxCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_PACKETUNICASTTXCOUNT = "Packet Unicast Tx Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETUNICASTTXCOUNT = "wifinetworkdiagnostics-packetunicasttxcount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_PACKETUNICASTTXCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_PACKETUNICASTTXCOUNT); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_CURRENTMAXRATE = "CurrentMaxRate"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_CURRENTMAXRATE = "Current Max Rate"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_CURRENTMAXRATE = "wifinetworkdiagnostics-currentmaxrate"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_CURRENTMAXRATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_CURRENTMAXRATE); + + public static final String CHANNEL_NAME_WIFINETWORKDIAGNOSTICS_OVERRUNCOUNT = "OverrunCount"; + public static final String CHANNEL_LABEL_WIFINETWORKDIAGNOSTICS_OVERRUNCOUNT = "Overrun Count"; + public static final String CHANNEL_ID_WIFINETWORKDIAGNOSTICS_OVERRUNCOUNT = "wifinetworkdiagnostics-overruncount"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKDIAGNOSTICS_OVERRUNCOUNT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKDIAGNOSTICS_OVERRUNCOUNT); + + // WiFiNetworkManagement Cluster + public static final String CHANNEL_NAME_WIFINETWORKMANAGEMENT_SSID = "Ssid"; + public static final String CHANNEL_LABEL_WIFINETWORKMANAGEMENT_SSID = "Ssid"; + public static final String CHANNEL_ID_WIFINETWORKMANAGEMENT_SSID = "wifinetworkmanagement-ssid"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKMANAGEMENT_SSID = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKMANAGEMENT_SSID); + + public static final String CHANNEL_NAME_WIFINETWORKMANAGEMENT_PASSPHRASESURROGATE = "PassphraseSurrogate"; + public static final String CHANNEL_LABEL_WIFINETWORKMANAGEMENT_PASSPHRASESURROGATE = "Passphrase Surrogate"; + public static final String CHANNEL_ID_WIFINETWORKMANAGEMENT_PASSPHRASESURROGATE = "wifinetworkmanagement-passphrasesurrogate"; + public static final ChannelTypeUID CHANNEL_WIFINETWORKMANAGEMENT_PASSPHRASESURROGATE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WIFINETWORKMANAGEMENT_PASSPHRASESURROGATE); + + // WindowCovering Cluster + public static final String CHANNEL_NAME_WINDOWCOVERING_TYPE = "Type"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_TYPE = "Type"; + public static final String CHANNEL_ID_WINDOWCOVERING_TYPE = "windowcovering-type"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_TYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_TYPE); + + public static final String CHANNEL_NAME_WINDOWCOVERING_PHYSICALCLOSEDLIMITLIFT = "PhysicalClosedLimitLift"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_PHYSICALCLOSEDLIMITLIFT = "Physical Closed Limit Lift"; + public static final String CHANNEL_ID_WINDOWCOVERING_PHYSICALCLOSEDLIMITLIFT = "windowcovering-physicalclosedlimitlift"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_PHYSICALCLOSEDLIMITLIFT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_PHYSICALCLOSEDLIMITLIFT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_PHYSICALCLOSEDLIMITTILT = "PhysicalClosedLimitTilt"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_PHYSICALCLOSEDLIMITTILT = "Physical Closed Limit Tilt"; + public static final String CHANNEL_ID_WINDOWCOVERING_PHYSICALCLOSEDLIMITTILT = "windowcovering-physicalclosedlimittilt"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_PHYSICALCLOSEDLIMITTILT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_PHYSICALCLOSEDLIMITTILT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CURRENTPOSITIONLIFT = "CurrentPositionLift"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CURRENTPOSITIONLIFT = "Current Position Lift"; + public static final String CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONLIFT = "windowcovering-currentpositionlift"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CURRENTPOSITIONLIFT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONLIFT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CURRENTPOSITIONTILT = "CurrentPositionTilt"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CURRENTPOSITIONTILT = "Current Position Tilt"; + public static final String CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONTILT = "windowcovering-currentpositiontilt"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CURRENTPOSITIONTILT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONTILT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_NUMBEROFACTUATIONSLIFT = "NumberOfActuationsLift"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_NUMBEROFACTUATIONSLIFT = "Number Of Actuations Lift"; + public static final String CHANNEL_ID_WINDOWCOVERING_NUMBEROFACTUATIONSLIFT = "windowcovering-numberofactuationslift"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_NUMBEROFACTUATIONSLIFT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_NUMBEROFACTUATIONSLIFT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_NUMBEROFACTUATIONSTILT = "NumberOfActuationsTilt"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_NUMBEROFACTUATIONSTILT = "Number Of Actuations Tilt"; + public static final String CHANNEL_ID_WINDOWCOVERING_NUMBEROFACTUATIONSTILT = "windowcovering-numberofactuationstilt"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_NUMBEROFACTUATIONSTILT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_NUMBEROFACTUATIONSTILT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CONFIGSTATUS = "ConfigStatus"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CONFIGSTATUS = "Config Status"; + public static final String CHANNEL_ID_WINDOWCOVERING_CONFIGSTATUS = "windowcovering-configstatus"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CONFIGSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CONFIGSTATUS); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENTAGE = "CurrentPositionLiftPercentage"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENTAGE = "Current Position Lift Percentage"; + public static final String CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENTAGE = "windowcovering-currentpositionliftpercentage"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENTAGE); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENTAGE = "CurrentPositionTiltPercentage"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENTAGE = "Current Position Tilt Percentage"; + public static final String CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENTAGE = "windowcovering-currentpositiontiltpercentage"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENTAGE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENTAGE); + + public static final String CHANNEL_NAME_WINDOWCOVERING_OPERATIONALSTATUS = "OperationalStatus"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_OPERATIONALSTATUS = "Operational Status"; + public static final String CHANNEL_ID_WINDOWCOVERING_OPERATIONALSTATUS = "windowcovering-operationalstatus"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_OPERATIONALSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_OPERATIONALSTATUS); + + public static final String CHANNEL_NAME_WINDOWCOVERING_TARGETPOSITIONLIFTPERCENT100THS = "TargetPositionLiftPercent100ths"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_TARGETPOSITIONLIFTPERCENT100THS = "Target Position Lift Percent100ths"; + public static final String CHANNEL_ID_WINDOWCOVERING_TARGETPOSITIONLIFTPERCENT100THS = "windowcovering-targetpositionliftpercent100ths"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_TARGETPOSITIONLIFTPERCENT100THS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_TARGETPOSITIONLIFTPERCENT100THS); + + public static final String CHANNEL_NAME_WINDOWCOVERING_TARGETPOSITIONTILTPERCENT100THS = "TargetPositionTiltPercent100ths"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_TARGETPOSITIONTILTPERCENT100THS = "Target Position Tilt Percent100ths"; + public static final String CHANNEL_ID_WINDOWCOVERING_TARGETPOSITIONTILTPERCENT100THS = "windowcovering-targetpositiontiltpercent100ths"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_TARGETPOSITIONTILTPERCENT100THS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_TARGETPOSITIONTILTPERCENT100THS); + + public static final String CHANNEL_NAME_WINDOWCOVERING_ENDPRODUCTTYPE = "EndProductType"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_ENDPRODUCTTYPE = "End Product Type"; + public static final String CHANNEL_ID_WINDOWCOVERING_ENDPRODUCTTYPE = "windowcovering-endproducttype"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_ENDPRODUCTTYPE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_ENDPRODUCTTYPE); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENT100THS = "CurrentPositionLiftPercent100ths"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENT100THS = "Current Position Lift Percent100ths"; + public static final String CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENT100THS = "windowcovering-currentpositionliftpercent100ths"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENT100THS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONLIFTPERCENT100THS); + + public static final String CHANNEL_NAME_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENT100THS = "CurrentPositionTiltPercent100ths"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENT100THS = "Current Position Tilt Percent100ths"; + public static final String CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENT100THS = "windowcovering-currentpositiontiltpercent100ths"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENT100THS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_CURRENTPOSITIONTILTPERCENT100THS); + + public static final String CHANNEL_NAME_WINDOWCOVERING_INSTALLEDOPENLIMITLIFT = "InstalledOpenLimitLift"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_INSTALLEDOPENLIMITLIFT = "Installed Open Limit Lift"; + public static final String CHANNEL_ID_WINDOWCOVERING_INSTALLEDOPENLIMITLIFT = "windowcovering-installedopenlimitlift"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_INSTALLEDOPENLIMITLIFT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_INSTALLEDOPENLIMITLIFT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_INSTALLEDCLOSEDLIMITLIFT = "InstalledClosedLimitLift"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_INSTALLEDCLOSEDLIMITLIFT = "Installed Closed Limit Lift"; + public static final String CHANNEL_ID_WINDOWCOVERING_INSTALLEDCLOSEDLIMITLIFT = "windowcovering-installedclosedlimitlift"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_INSTALLEDCLOSEDLIMITLIFT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_INSTALLEDCLOSEDLIMITLIFT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_INSTALLEDOPENLIMITTILT = "InstalledOpenLimitTilt"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_INSTALLEDOPENLIMITTILT = "Installed Open Limit Tilt"; + public static final String CHANNEL_ID_WINDOWCOVERING_INSTALLEDOPENLIMITTILT = "windowcovering-installedopenlimittilt"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_INSTALLEDOPENLIMITTILT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_INSTALLEDOPENLIMITTILT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_INSTALLEDCLOSEDLIMITTILT = "InstalledClosedLimitTilt"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_INSTALLEDCLOSEDLIMITTILT = "Installed Closed Limit Tilt"; + public static final String CHANNEL_ID_WINDOWCOVERING_INSTALLEDCLOSEDLIMITTILT = "windowcovering-installedclosedlimittilt"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_INSTALLEDCLOSEDLIMITTILT = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_INSTALLEDCLOSEDLIMITTILT); + + public static final String CHANNEL_NAME_WINDOWCOVERING_MODE = "Mode"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_MODE = "Mode"; + public static final String CHANNEL_ID_WINDOWCOVERING_MODE = "windowcovering-mode"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_MODE = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_MODE); + + public static final String CHANNEL_NAME_WINDOWCOVERING_SAFETYSTATUS = "SafetyStatus"; + public static final String CHANNEL_LABEL_WINDOWCOVERING_SAFETYSTATUS = "Safety Status"; + public static final String CHANNEL_ID_WINDOWCOVERING_SAFETYSTATUS = "windowcovering-safetystatus"; + public static final ChannelTypeUID CHANNEL_WINDOWCOVERING_SAFETYSTATUS = new ChannelTypeUID( + "matter:" + CHANNEL_ID_WINDOWCOVERING_SAFETYSTATUS); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ClusterRegistry.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ClusterRegistry.java new file mode 100644 index 00000000000..6ff6bf0d42d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ClusterRegistry.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * ClusterRegistry + * + * @author Dan Cunningham - Initial contribution + */ +public class ClusterRegistry { + + public static final Map> CLUSTER_IDS = new HashMap<>(); + static { + CLUSTER_IDS.put(31, AccessControlCluster.class); + CLUSTER_IDS.put(1294, AccountLoginCluster.class); + CLUSTER_IDS.put(37, ActionsCluster.class); + CLUSTER_IDS.put(114, ActivatedCarbonFilterMonitoringCluster.class); + CLUSTER_IDS.put(60, AdministratorCommissioningCluster.class); + CLUSTER_IDS.put(91, AirQualityCluster.class); + CLUSTER_IDS.put(1293, ApplicationBasicCluster.class); + CLUSTER_IDS.put(1292, ApplicationLauncherCluster.class); + CLUSTER_IDS.put(1291, AudioOutputCluster.class); + CLUSTER_IDS.put(769, BallastConfigurationCluster.class); + CLUSTER_IDS.put(40, BasicInformationCluster.class); + CLUSTER_IDS.put(30, BindingCluster.class); + CLUSTER_IDS.put(69, BooleanStateCluster.class); + CLUSTER_IDS.put(128, BooleanStateConfigurationCluster.class); + CLUSTER_IDS.put(57, BridgedDeviceBasicInformationCluster.class); + CLUSTER_IDS.put(1037, CarbonDioxideConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(1036, CarbonMonoxideConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(1284, ChannelCluster.class); + CLUSTER_IDS.put(768, ColorControlCluster.class); + CLUSTER_IDS.put(1873, CommissionerControlCluster.class); + CLUSTER_IDS.put(1296, ContentAppObserverCluster.class); + CLUSTER_IDS.put(1295, ContentControlCluster.class); + CLUSTER_IDS.put(1290, ContentLauncherCluster.class); + CLUSTER_IDS.put(29, DescriptorCluster.class); + CLUSTER_IDS.put(152, DeviceEnergyManagementCluster.class); + CLUSTER_IDS.put(159, DeviceEnergyManagementModeCluster.class); + CLUSTER_IDS.put(50, DiagnosticLogsCluster.class); + CLUSTER_IDS.put(93, DishwasherAlarmCluster.class); + CLUSTER_IDS.put(89, DishwasherModeCluster.class); + CLUSTER_IDS.put(257, DoorLockCluster.class); + CLUSTER_IDS.put(1872, EcosystemInformationCluster.class); + CLUSTER_IDS.put(145, ElectricalEnergyMeasurementCluster.class); + CLUSTER_IDS.put(144, ElectricalPowerMeasurementCluster.class); + CLUSTER_IDS.put(153, EnergyEvseCluster.class); + CLUSTER_IDS.put(157, EnergyEvseModeCluster.class); + CLUSTER_IDS.put(155, EnergyPreferenceCluster.class); + CLUSTER_IDS.put(55, EthernetNetworkDiagnosticsCluster.class); + CLUSTER_IDS.put(514, FanControlCluster.class); + CLUSTER_IDS.put(64, FixedLabelCluster.class); + CLUSTER_IDS.put(1028, FlowMeasurementCluster.class); + CLUSTER_IDS.put(1067, FormaldehydeConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(48, GeneralCommissioningCluster.class); + CLUSTER_IDS.put(51, GeneralDiagnosticsCluster.class); + CLUSTER_IDS.put(63, GroupKeyManagementCluster.class); + CLUSTER_IDS.put(4, GroupsCluster.class); + CLUSTER_IDS.put(113, HepaFilterMonitoringCluster.class); + CLUSTER_IDS.put(70, IcdManagementCluster.class); + CLUSTER_IDS.put(3, IdentifyCluster.class); + CLUSTER_IDS.put(1024, IlluminanceMeasurementCluster.class); + CLUSTER_IDS.put(1874, JointFabricDatastoreCluster.class); + CLUSTER_IDS.put(1875, JointFabricPkiCluster.class); + CLUSTER_IDS.put(1289, KeypadInputCluster.class); + CLUSTER_IDS.put(74, LaundryDryerControlsCluster.class); + CLUSTER_IDS.put(83, LaundryWasherControlsCluster.class); + CLUSTER_IDS.put(81, LaundryWasherModeCluster.class); + CLUSTER_IDS.put(8, LevelControlCluster.class); + CLUSTER_IDS.put(43, LocalizationConfigurationCluster.class); + CLUSTER_IDS.put(1288, LowPowerCluster.class); + CLUSTER_IDS.put(1287, MediaInputCluster.class); + CLUSTER_IDS.put(1286, MediaPlaybackCluster.class); + CLUSTER_IDS.put(95, MicrowaveOvenControlCluster.class); + CLUSTER_IDS.put(94, MicrowaveOvenModeCluster.class); + CLUSTER_IDS.put(80, ModeSelectCluster.class); + CLUSTER_IDS.put(49, NetworkCommissioningCluster.class); + CLUSTER_IDS.put(1043, NitrogenDioxideConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(1030, OccupancySensingCluster.class); + CLUSTER_IDS.put(6, OnOffCluster.class); + CLUSTER_IDS.put(62, OperationalCredentialsCluster.class); + CLUSTER_IDS.put(96, OperationalStateCluster.class); + CLUSTER_IDS.put(41, OtaSoftwareUpdateProviderCluster.class); + CLUSTER_IDS.put(42, OtaSoftwareUpdateRequestorCluster.class); + CLUSTER_IDS.put(72, OvenCavityOperationalStateCluster.class); + CLUSTER_IDS.put(73, OvenModeCluster.class); + CLUSTER_IDS.put(1045, OzoneConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(1069, Pm10ConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(1068, Pm1ConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(1066, Pm25ConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(47, PowerSourceCluster.class); + CLUSTER_IDS.put(46, PowerSourceConfigurationCluster.class); + CLUSTER_IDS.put(156, PowerTopologyCluster.class); + CLUSTER_IDS.put(1027, PressureMeasurementCluster.class); + CLUSTER_IDS.put(66, ProxyConfigurationCluster.class); + CLUSTER_IDS.put(67, ProxyDiscoveryCluster.class); + CLUSTER_IDS.put(512, PumpConfigurationAndControlCluster.class); + CLUSTER_IDS.put(1071, RadonConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(87, RefrigeratorAlarmCluster.class); + CLUSTER_IDS.put(82, RefrigeratorAndTemperatureControlledCabinetModeCluster.class); + CLUSTER_IDS.put(1029, RelativeHumidityMeasurementCluster.class); + CLUSTER_IDS.put(85, RvcCleanModeCluster.class); + CLUSTER_IDS.put(97, RvcOperationalStateCluster.class); + CLUSTER_IDS.put(84, RvcRunModeCluster.class); + CLUSTER_IDS.put(98, ScenesManagementCluster.class); + CLUSTER_IDS.put(336, ServiceAreaCluster.class); + CLUSTER_IDS.put(92, SmokeCoAlarmCluster.class); + CLUSTER_IDS.put(52, SoftwareDiagnosticsCluster.class); + CLUSTER_IDS.put(59, SwitchCluster.class); + CLUSTER_IDS.put(1285, TargetNavigatorCluster.class); + CLUSTER_IDS.put(86, TemperatureControlCluster.class); + CLUSTER_IDS.put(1026, TemperatureMeasurementCluster.class); + CLUSTER_IDS.put(513, ThermostatCluster.class); + CLUSTER_IDS.put(516, ThermostatUserInterfaceConfigurationCluster.class); + CLUSTER_IDS.put(1106, ThreadBorderRouterManagementCluster.class); + CLUSTER_IDS.put(53, ThreadNetworkDiagnosticsCluster.class); + CLUSTER_IDS.put(1107, ThreadNetworkDirectoryCluster.class); + CLUSTER_IDS.put(44, TimeFormatLocalizationCluster.class); + CLUSTER_IDS.put(56, TimeSynchronizationCluster.class); + CLUSTER_IDS.put(1070, TotalVolatileOrganicCompoundsConcentrationMeasurementCluster.class); + CLUSTER_IDS.put(45, UnitLocalizationCluster.class); + CLUSTER_IDS.put(65, UserLabelCluster.class); + CLUSTER_IDS.put(68, ValidProxiesCluster.class); + CLUSTER_IDS.put(129, ValveConfigurationAndControlCluster.class); + CLUSTER_IDS.put(1283, WakeOnLanCluster.class); + CLUSTER_IDS.put(148, WaterHeaterManagementCluster.class); + CLUSTER_IDS.put(158, WaterHeaterModeCluster.class); + CLUSTER_IDS.put(121, WaterTankLevelMonitoringCluster.class); + CLUSTER_IDS.put(54, WiFiNetworkDiagnosticsCluster.class); + CLUSTER_IDS.put(1105, WiFiNetworkManagementCluster.class); + CLUSTER_IDS.put(258, WindowCoveringCluster.class); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ColorControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ColorControlCluster.java new file mode 100644 index 00000000000..fbb24fcee19 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ColorControlCluster.java @@ -0,0 +1,1182 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ColorControl + * + * @author Dan Cunningham - Initial contribution + */ +public class ColorControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0300; + public static final String CLUSTER_NAME = "ColorControl"; + public static final String CLUSTER_PREFIX = "colorControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CURRENT_HUE = "currentHue"; + public static final String ATTRIBUTE_CURRENT_SATURATION = "currentSaturation"; + public static final String ATTRIBUTE_REMAINING_TIME = "remainingTime"; + public static final String ATTRIBUTE_CURRENT_X = "currentX"; + public static final String ATTRIBUTE_CURRENT_Y = "currentY"; + public static final String ATTRIBUTE_DRIFT_COMPENSATION = "driftCompensation"; + public static final String ATTRIBUTE_COMPENSATION_TEXT = "compensationText"; + public static final String ATTRIBUTE_COLOR_TEMPERATURE_MIREDS = "colorTemperatureMireds"; + public static final String ATTRIBUTE_COLOR_MODE = "colorMode"; + public static final String ATTRIBUTE_OPTIONS = "options"; + public static final String ATTRIBUTE_NUMBER_OF_PRIMARIES = "numberOfPrimaries"; + public static final String ATTRIBUTE_PRIMARY1X = "primary1X"; + public static final String ATTRIBUTE_PRIMARY1Y = "primary1Y"; + public static final String ATTRIBUTE_PRIMARY1INTENSITY = "primary1Intensity"; + public static final String ATTRIBUTE_PRIMARY2X = "primary2X"; + public static final String ATTRIBUTE_PRIMARY2Y = "primary2Y"; + public static final String ATTRIBUTE_PRIMARY2INTENSITY = "primary2Intensity"; + public static final String ATTRIBUTE_PRIMARY3X = "primary3X"; + public static final String ATTRIBUTE_PRIMARY3Y = "primary3Y"; + public static final String ATTRIBUTE_PRIMARY3INTENSITY = "primary3Intensity"; + public static final String ATTRIBUTE_PRIMARY4X = "primary4X"; + public static final String ATTRIBUTE_PRIMARY4Y = "primary4Y"; + public static final String ATTRIBUTE_PRIMARY4INTENSITY = "primary4Intensity"; + public static final String ATTRIBUTE_PRIMARY5X = "primary5X"; + public static final String ATTRIBUTE_PRIMARY5Y = "primary5Y"; + public static final String ATTRIBUTE_PRIMARY5INTENSITY = "primary5Intensity"; + public static final String ATTRIBUTE_PRIMARY6X = "primary6X"; + public static final String ATTRIBUTE_PRIMARY6Y = "primary6Y"; + public static final String ATTRIBUTE_PRIMARY6INTENSITY = "primary6Intensity"; + public static final String ATTRIBUTE_WHITE_POINT_X = "whitePointX"; + public static final String ATTRIBUTE_WHITE_POINT_Y = "whitePointY"; + public static final String ATTRIBUTE_COLOR_POINT_RX = "colorPointRx"; + public static final String ATTRIBUTE_COLOR_POINT_RY = "colorPointRy"; + public static final String ATTRIBUTE_COLOR_POINT_RINTENSITY = "colorPointRIntensity"; + public static final String ATTRIBUTE_COLOR_POINT_GX = "colorPointGx"; + public static final String ATTRIBUTE_COLOR_POINT_GY = "colorPointGy"; + public static final String ATTRIBUTE_COLOR_POINT_GINTENSITY = "colorPointGIntensity"; + public static final String ATTRIBUTE_COLOR_POINT_BX = "colorPointBx"; + public static final String ATTRIBUTE_COLOR_POINT_BY = "colorPointBy"; + public static final String ATTRIBUTE_COLOR_POINT_BINTENSITY = "colorPointBIntensity"; + public static final String ATTRIBUTE_ENHANCED_CURRENT_HUE = "enhancedCurrentHue"; + public static final String ATTRIBUTE_ENHANCED_COLOR_MODE = "enhancedColorMode"; + public static final String ATTRIBUTE_COLOR_LOOP_ACTIVE = "colorLoopActive"; + public static final String ATTRIBUTE_COLOR_LOOP_DIRECTION = "colorLoopDirection"; + public static final String ATTRIBUTE_COLOR_LOOP_TIME = "colorLoopTime"; + public static final String ATTRIBUTE_COLOR_LOOP_START_ENHANCED_HUE = "colorLoopStartEnhancedHue"; + public static final String ATTRIBUTE_COLOR_LOOP_STORED_ENHANCED_HUE = "colorLoopStoredEnhancedHue"; + public static final String ATTRIBUTE_COLOR_CAPABILITIES = "colorCapabilities"; + public static final String ATTRIBUTE_COLOR_TEMP_PHYSICAL_MIN_MIREDS = "colorTempPhysicalMinMireds"; + public static final String ATTRIBUTE_COLOR_TEMP_PHYSICAL_MAX_MIREDS = "colorTempPhysicalMaxMireds"; + public static final String ATTRIBUTE_COUPLE_COLOR_TEMP_TO_LEVEL_MIN_MIREDS = "coupleColorTempToLevelMinMireds"; + public static final String ATTRIBUTE_START_UP_COLOR_TEMPERATURE_MIREDS = "startUpColorTemperatureMireds"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The CurrentHue attribute contains the current hue value of the light. It is updated as fast as practical during + * commands that change the hue. + * The hue in degrees shall be related to the CurrentHue attribute by the relationship: + * Hue = "CurrentHue" * 360 / 254 + * where CurrentHue is in the range from 0 to 254 inclusive. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second or + * • At the end of the movement/transition. + */ + public Integer currentHue; // 0 uint8 R V + /** + * Indicates the current saturation value of the light. It is updated as fast as practical during commands that + * change the saturation. + * The saturation (on a scale from 0.0 to 1.0) shall be related to the CurrentSaturation attribute by the + * relationship: + * Saturation = "CurrentSaturation" / 254 + * where CurrentSaturation is in the range from 0 to 254 inclusive. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second or + * • At the end of the movement/transition. + */ + public Integer currentSaturation; // 1 uint8 R V + /** + * Indicates the time remaining, in 1/10ths of a second, until transitions due to the currently active command will + * be complete. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • When it changes from 0 to any value higher than 10, or + * • When it changes, with a delta larger than 10, caused by the invoke of a command, or + * • When it changes to 0. + * For commands with a transition time or changes to the transition time less than 1 second, changes to this + * attribute shall NOT be reported. + * As this attribute is not being reported during a regular countdown, clients SHOULD NOT rely on the reporting of + * this attribute in order to keep track of the remaining duration. + */ + public Integer remainingTime; // 2 uint16 R V + /** + * Indicates the current value of the normalized chromaticity value x, as defined in the CIE xyY Color Space. It is + * updated as fast as practical during commands that change the color. + * The value of x shall be related to the CurrentX attribute by the relationship + * x = "CurrentX" / 65536 + * where CurrentX is in the range from 0 to 65279 inclusive. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second or + * • At the end of the movement/transition. + */ + public Integer currentX; // 3 uint16 R V + /** + * Indicates the current value of the normalized chromaticity value y, as defined in the CIE xyY Color Space. It is + * updated as fast as practical during commands that change the color. + * The value of y shall be related to the CurrentY attribute by the relationship + * y = "CurrentY" / 65536 + * where CurrentY is in the range from 0 to 65279 inclusive. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second or + * • At the end of the movement/transition. + */ + public Integer currentY; // 4 uint16 R V + /** + * This attribute shall indicate what mechanism, if any, is in use for compensation for color/intensity drift over + * time. + */ + public DriftCompensationEnum driftCompensation; // 5 DriftCompensationEnum R V + /** + * This attribute shall contain a textual indication of what mechanism, if any, is in use to compensate for + * color/intensity drift over time. + */ + public String compensationText; // 6 string R V + /** + * Indicates a scaled inverse of the current value of the color temperature. The unit of ColorTemperatureMireds is + * the mired (micro reciprocal degree), a.k.a. mirek (micro reciprocal kelvin). It is updated as fast as practical + * during commands that change the color. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second or + * • At the end of the movement/transition. + * The color temperature value in kelvins shall be related to the ColorTemperatureMireds attribute in mired by the + * relationship + * "Color temperature [K]" = "1,000,000" / "ColorTemperatureMireds" + * where ColorTemperatureMireds is in the range from 1 to 65279 inclusive, giving a color temperature range from + * 1,000,000 K to 15.32 K. + * If this attribute is implemented then the ColorMode attribute shall also be implemented. + */ + public Integer colorTemperatureMireds; // 7 uint16 R V + /** + * Indicates which attributes are currently determining the color of the device. + * The value of the ColorMode attribute cannot be written directly - it is set upon reception of any command in + * section Commands to the appropriate mode for that command. + */ + public ColorModeEnum colorMode; // 8 ColorModeEnum R V + /** + * Indicates a bitmap that determines the default behavior of some cluster commands. Each command that is dependent + * on the Options attribute shall first construct a temporary Options bitmap that is in effect during the command + * processing. The temporary Options bitmap has the same format and meaning as the Options attribute, but includes + * any bits that may be overridden by command fields. + * This attribute is meant to be changed only during commissioning. + * Below is the format and description of the Options attribute and temporary Options bitmap and the effect on + * dependent commands. + * Command execution shall NOT continue beyond the Options processing if all of these criteria are true: + * • The On/Off cluster exists on the same endpoint as this cluster. + * • The OnOff attribute of the On/Off cluster, on this endpoint, is FALSE. + * • The value of the ExecuteIfOff bit is 0. + */ + public OptionsBitmap options; // 15 OptionsBitmap RW VO + /** + * Indicates the number of color primaries implemented on this device. A value of null shall indicate that the + * number of primaries is unknown. + * Where this attribute is implemented, the attributes below for indicating the “x” and “y” color values of the + * primaries shall also be implemented for each of the primaries from 1 to NumberOfPrimaries, without leaving gaps. + * Implementation of the Primary1Intensity attribute and subsequent intensity attributes is optional. + */ + public Integer numberOfPrimaries; // 16 uint8 R V + /** + * Indicates the normalized chromaticity value x for this primary, as defined in the CIE xyY Color Space. + * The value of x shall be related to the Primary1X attribute by the relationship x = Primary1X / 65536 + * (Primary1X in the range 0 to 65279 inclusive) + */ + public Integer primary1X; // 17 uint16 R V + /** + * Indicates the normalized chromaticity value y for this primary, as defined in the CIE xyY Color Space. + * The value of y shall be related to the Primary1Y attribute by the relationship y = Primary1Y / 65536 + * (Primary1Y in the range 0 to 65279 inclusive) + */ + public Integer primary1Y; // 18 uint16 R V + /** + * Indicates a representation of the maximum intensity of this primary as defined in the Dimming Light Curve in the + * Ballast Configuration cluster (see Ballast Configuration Cluster), normalized such that the primary with the + * highest maximum intensity contains the value 254. + * A value of null shall indicate that this primary is not available. + */ + public Integer primary1Intensity; // 19 uint8 R V + public Integer primary2X; // 21 uint16 R V + public Integer primary2Y; // 22 uint16 R V + public Integer primary2Intensity; // 23 uint8 R V + public Integer primary3X; // 25 uint16 R V + public Integer primary3Y; // 26 uint16 R V + public Integer primary3Intensity; // 27 uint8 R V + public Integer primary4X; // 32 uint16 R V + public Integer primary4Y; // 33 uint16 R V + public Integer primary4Intensity; // 34 uint8 R V + public Integer primary5X; // 36 uint16 R V + public Integer primary5Y; // 37 uint16 R V + public Integer primary5Intensity; // 38 uint8 R V + public Integer primary6X; // 40 uint16 R V + public Integer primary6Y; // 41 uint16 R V + public Integer primary6Intensity; // 42 uint8 R V + /** + * Indicates the normalized chromaticity value x, as defined in the CIE xyY Color Space, of the current white point + * of the device. + * The value of x shall be related to the WhitePointX attribute by the relationship x = WhitePointX / 65536 + * (WhitePointX in the range 0 to 65279 inclusive) + */ + public Integer whitePointX; // 48 uint16 RW VM + /** + * Indicates the normalized chromaticity value y, as defined in the CIE xyY Color Space, of the current white point + * of the device. + * The value of y shall be related to the WhitePointY attribute by the relationship y = WhitePointY / 65536 + * (WhitePointY in the range 0 to 65279 inclusive) + */ + public Integer whitePointY; // 49 uint16 RW VM + /** + * Indicates the normalized chromaticity value x, as defined in the CIE xyY Color Space, of the red color point of + * the device. + * The value of x shall be related to the ColorPointRX attribute by the relationship x = ColorPointRX / 65536 + * (ColorPointRX in the range 0 to 65279 inclusive) + */ + public Integer colorPointRx; // 50 uint16 RW VM + /** + * Indicates the normalized chromaticity value y, as defined in the CIE xyY Color Space, of the red color point of + * the device. + * The value of y shall be related to the ColorPointRY attribute by the relationship y = ColorPointRY / 65536 + * (ColorPointRY in the range 0 to 65279 inclusive) + */ + public Integer colorPointRy; // 51 uint16 RW VM + /** + * Indicates a representation of the relative intensity of the red color point as defined in the Dimming Light Curve + * in the Ballast Configuration cluster (see Ballast Configuration Cluster), normalized such that the color point + * with the highest relative intensity contains the value 254. + * A value of null shall indicate an invalid value. + */ + public Integer colorPointRIntensity; // 52 uint8 RW VM + public Integer colorPointGx; // 54 uint16 RW VM + public Integer colorPointGy; // 55 uint16 RW VM + public Integer colorPointGIntensity; // 56 uint8 RW VM + public Integer colorPointBx; // 58 uint16 RW VM + public Integer colorPointBy; // 59 uint16 RW VM + public Integer colorPointBIntensity; // 60 uint8 RW VM + /** + * Indicates the non-equidistant steps along the CIE 1931 color triangle, and it provides 16-bits precision. + * The upper 8 bits of this attribute shall be used as an index in the implementation specific XY lookup table to + * provide the non-equidistant steps. The lower 8 bits shall be used to interpolate between these steps in a linear + * way in order to provide color zoom for the user. + * To provide compatibility with clients not supporting EHUE, the CurrentHue attribute shall contain a hue value in + * the range 0 to 254, calculated from the EnhancedCurrentHue attribute. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second or + * • At the end of the movement/transition. + */ + public Integer enhancedCurrentHue; // 16384 uint16 R V + /** + * Indicates which attributes are currently determining the color of the device. + * To provide compatibility with clients not supporting EHUE, the original ColorMode attribute shall indicate + * CurrentHue and CurrentSaturation when the light uses the EnhancedCurrentHue attribute. If the ColorMode attribute + * is changed, its new value shall be copied to the EnhancedColorMode attribute. + */ + public EnhancedColorModeEnum enhancedColorMode; // 16385 EnhancedColorModeEnum R V + /** + * Indicates the current active status of the color loop. If this attribute has the value 0, the color loop shall + * NOT be active. If this attribute has the value 1, the color loop shall be active. + */ + public ColorLoopActive colorLoopActive; // 16386 enum16 R V + /** + * Indicates the current direction of the color loop. If this attribute has the value 0, the EnhancedCurrentHue + * attribute shall be decremented. If this attribute has the value 1, the EnhancedCurrentHue attribute shall be + * incremented. + */ + public ColorLoopDirectionEnum colorLoopDirection; // 16387 ColorLoopDirectionEnum R V + /** + * Indicates the number of seconds it shall take to perform a full color loop, i.e., to cycle all values of the + * EnhancedCurrentHue attribute (between 0 and 65534). + */ + public Integer colorLoopTime; // 16388 uint16 R V + /** + * Indicates the value of the EnhancedCurrentHue attribute from which the color loop shall be started. + */ + public Integer colorLoopStartEnhancedHue; // 16389 uint16 R V + /** + * Indicates the value of the EnhancedCurrentHue attribute before the color loop was started. Once the color loop is + * complete, the EnhancedCurrentHue attribute shall be restored to this value. + */ + public Integer colorLoopStoredEnhancedHue; // 16390 uint16 R V + /** + * Indicates the color control capabilities of the device. + * Bits 0-4 of the ColorCapabilities attribute shall have the same values as the corresponding bits of the + * FeatureMap attribute. All other bits in ColorCapabilities shall be 0. + */ + public ColorCapabilities colorCapabilities; // 16394 map16 R V + /** + * This attribute shall indicate the minimum mired value supported by the hardware. ColorTempPhysicalMinMireds + * corresponds to the maximum color temperature in kelvins supported by the hardware. + * ColorTempPhysicalMinMireds <= ColorTemperatureMireds. + */ + public Integer colorTempPhysicalMinMireds; // 16395 uint16 R V + /** + * This attribute shall indicate the maximum mired value supported by the hardware. ColorTempPhysicalMaxMireds + * corresponds to the minimum color temperature in kelvins supported by the hardware. + * ColorTemperatureMireds <= ColorTempPhysicalMaxMireds. + */ + public Integer colorTempPhysicalMaxMireds; // 16396 uint16 R V + /** + * Indicates a lower bound on the value of the ColorTemperatureMireds attribute for the purposes of coupling the + * ColorTemperatureMireds attribute to the CurrentLevel attribute when the CoupleColorTempToLevel bit of the Options + * attribute of the Level Control cluster is equal to 1. When coupling the ColorTemperatureMireds attribute to the + * CurrentLevel attribute, this value shall correspond to a CurrentLevel value of 254 (100%). + * This attribute shall be set such that the following relationship exists: ColorTempPhysicalMinMireds <= + * CoupleColorTempToLevelMinMireds <= ColorTemperatureMireds + * Note that since this attribute is stored as a micro reciprocal degree (mired) value (i.e. color temperature in + * kelvins = 1,000,000 / CoupleColorTempToLevelMinMireds), the CoupleColorTempToLevelMinMireds attribute + * corresponds to an upper bound on the value of the color temperature in kelvins supported by the device. + */ + public Integer coupleColorTempToLevelMinMireds; // 16397 uint16 R V + /** + * Indicates the desired startup color temperature value the light shall use when it is supplied with power and this + * value shall be reflected in the ColorTemperatureMireds attribute. In addition, the ColorMode and + * EnhancedColorMode attributes shall be set to 2 (ColorTemperatureMireds). The values of the + * StartUpColorTemperatureMireds attribute are listed in the table below, + */ + public Integer startUpColorTemperatureMireds; // 16400 uint16 RW VM + + // Enums + /** + * Indicates the current active status of the color loop. If this attribute has the value 0, the color loop shall + * NOT be active. If this attribute has the value 1, the color loop shall be active. + */ + public enum ColorLoopActive implements MatterEnum { + INACTIVE(0, "Inactive"), + ACTIVE(1, "Active"); + + public final Integer value; + public final String label; + + private ColorLoopActive(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum DriftCompensationEnum implements MatterEnum { + NONE(0, "None"), + OTHER_OR_UNKNOWN(1, "Other Or Unknown"), + TEMPERATURE_MONITORING(2, "Temperature Monitoring"), + OPTICAL_LUMINANCE_MONITORING_AND_FEEDBACK(3, "Optical Luminance Monitoring And Feedback"), + OPTICAL_COLOR_MONITORING_AND_FEEDBACK(4, "Optical Color Monitoring And Feedback"); + + public final Integer value; + public final String label; + + private DriftCompensationEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ColorModeEnum implements MatterEnum { + CURRENT_HUE_AND_CURRENT_SATURATION(0, "Current Hue And Current Saturation"), + CURRENT_X_AND_CURRENT_Y(1, "Current X And Current Y"), + COLOR_TEMPERATURE_MIREDS(2, "Color Temperature Mireds"); + + public final Integer value; + public final String label; + + private ColorModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EnhancedColorModeEnum implements MatterEnum { + CURRENT_HUE_AND_CURRENT_SATURATION(0, "Current Hue And Current Saturation"), + CURRENT_X_AND_CURRENT_Y(1, "Current X And Current Y"), + COLOR_TEMPERATURE_MIREDS(2, "Color Temperature Mireds"), + ENHANCED_CURRENT_HUE_AND_CURRENT_SATURATION(3, "Enhanced Current Hue And Current Saturation"); + + public final Integer value; + public final String label; + + private EnhancedColorModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum DirectionEnum implements MatterEnum { + SHORTEST(0, "Shortest"), + LONGEST(1, "Longest"), + UP(2, "Up"), + DOWN(3, "Down"); + + public final Integer value; + public final String label; + + private DirectionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum MoveModeEnum implements MatterEnum { + STOP(0, "Stop"), + UP(1, "Up"), + DOWN(3, "Down"); + + public final Integer value; + public final String label; + + private MoveModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StepModeEnum implements MatterEnum { + UP(1, "Up"), + DOWN(3, "Down"); + + public final Integer value; + public final String label; + + private StepModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ColorLoopActionEnum implements MatterEnum { + DEACTIVATE(0, "Deactivate"), + ACTIVATE_FROM_COLOR_LOOP_START_ENHANCED_HUE(1, "Activate From Color Loop Start Enhanced Hue"), + ACTIVATE_FROM_ENHANCED_CURRENT_HUE(2, "Activate From Enhanced Current Hue"); + + public final Integer value; + public final String label; + + private ColorLoopActionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ColorLoopDirectionEnum implements MatterEnum { + DECREMENT(0, "Decrement"), + INCREMENT(1, "Increment"); + + public final Integer value; + public final String label; + + private ColorLoopDirectionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + /** + * Indicates the color control capabilities of the device. + * Bits 0-4 of the ColorCapabilities attribute shall have the same values as the corresponding bits of the + * FeatureMap attribute. All other bits in ColorCapabilities shall be 0. + */ + public static class ColorCapabilities { + public boolean hueSaturation; + public boolean enhancedHue; + public boolean colorLoop; + public boolean xY; + public boolean colorTemperature; + + public ColorCapabilities(boolean hueSaturation, boolean enhancedHue, boolean colorLoop, boolean xY, + boolean colorTemperature) { + this.hueSaturation = hueSaturation; + this.enhancedHue = enhancedHue; + this.colorLoop = colorLoop; + this.xY = xY; + this.colorTemperature = colorTemperature; + } + } + + public static class OptionsBitmap { + /** + * Dependency on On/Off cluster + * This bit shall indicate if this cluster server instance has a dependency with the On/Off cluster. + */ + public boolean executeIfOff; + + public OptionsBitmap(boolean executeIfOff) { + this.executeIfOff = executeIfOff; + } + } + + /** + * This data type is derived from map8 and is used in the ColorLoopSet command. + */ + public static class UpdateFlagsBitmap { + /** + * Device adheres to the associated action field. + * This bit shall indicate whether the server adheres to the Action field in order to process the command. + * • 0 = Device shall ignore the Action field. + * • 1 = Device shall adhere to the Action field. + */ + public boolean updateAction; + /** + * Device updates the associated direction attribute. + * This bit shall indicate whether the device updates the ColorLoopDirection attribute with the Direction field. + * • 0 = Device shall ignore the Direction field. + * • 1 = Device shall update the ColorLoopDirection attribute with the value of the Direction field. + */ + public boolean updateDirection; + /** + * Device updates the associated time attribute. + * This bit shall indicate whether the device updates the ColorLoopTime attribute with the Time field. + * • 0 = Device shall ignore the Time field. + * • 1 = Device shall update the value of the ColorLoopTime attribute with the value of the Time field. + */ + public boolean updateTime; + /** + * Device updates the associated start hue attribute. + * This bit shall indicate whether the device updates the ColorLoopStartEnhancedHue attribute with the value of + * the StartHue field. + * • 0 = Device shall ignore the StartHue field. + * • 1 = Device shall update the value of the ColorLoopStartEnhancedHue attribute with the value of the + * StartHue field. + */ + public boolean updateStartHue; + + public UpdateFlagsBitmap(boolean updateAction, boolean updateDirection, boolean updateTime, + boolean updateStartHue) { + this.updateAction = updateAction; + this.updateDirection = updateDirection; + this.updateTime = updateTime; + this.updateStartHue = updateStartHue; + } + } + + public static class FeatureMap { + /** + * + * Supports color specification via hue/saturation. + */ + public boolean hueSaturation; + /** + * + * Enhanced hue is supported. + */ + public boolean enhancedHue; + /** + * + * Color loop is supported. + */ + public boolean colorLoop; + /** + * + * Supports color specification via XY. + */ + public boolean xy; + /** + * + * Supports specification of color temperature. + */ + public boolean colorTemperature; + + public FeatureMap(boolean hueSaturation, boolean enhancedHue, boolean colorLoop, boolean xy, + boolean colorTemperature) { + this.hueSaturation = hueSaturation; + this.enhancedHue = enhancedHue; + this.colorLoop = colorLoop; + this.xy = xy; + this.colorTemperature = colorTemperature; + } + } + + public ColorControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 768, "ColorControl"); + } + + protected ColorControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand moveToHue(Integer hue, DirectionEnum direction, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (hue != null) { + map.put("hue", hue); + } + if (direction != null) { + map.put("direction", direction); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToHue", map); + } + + public static ClusterCommand moveHue(MoveModeEnum moveMode, Integer rate, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (moveMode != null) { + map.put("moveMode", moveMode); + } + if (rate != null) { + map.put("rate", rate); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveHue", map); + } + + public static ClusterCommand stepHue(StepModeEnum stepMode, Integer stepSize, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepMode != null) { + map.put("stepMode", stepMode); + } + if (stepSize != null) { + map.put("stepSize", stepSize); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stepHue", map); + } + + public static ClusterCommand moveToSaturation(Integer saturation, Integer transitionTime, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (saturation != null) { + map.put("saturation", saturation); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToSaturation", map); + } + + public static ClusterCommand moveSaturation(MoveModeEnum moveMode, Integer rate, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (moveMode != null) { + map.put("moveMode", moveMode); + } + if (rate != null) { + map.put("rate", rate); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveSaturation", map); + } + + public static ClusterCommand stepSaturation(StepModeEnum stepMode, Integer stepSize, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepMode != null) { + map.put("stepMode", stepMode); + } + if (stepSize != null) { + map.put("stepSize", stepSize); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stepSaturation", map); + } + + public static ClusterCommand moveToHueAndSaturation(Integer hue, Integer saturation, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (hue != null) { + map.put("hue", hue); + } + if (saturation != null) { + map.put("saturation", saturation); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToHueAndSaturation", map); + } + + public static ClusterCommand moveToColor(Integer colorX, Integer colorY, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (colorX != null) { + map.put("colorX", colorX); + } + if (colorY != null) { + map.put("colorY", colorY); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToColor", map); + } + + public static ClusterCommand moveColor(Integer rateX, Integer rateY, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (rateX != null) { + map.put("rateX", rateX); + } + if (rateY != null) { + map.put("rateY", rateY); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveColor", map); + } + + public static ClusterCommand stepColor(Integer stepX, Integer stepY, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepX != null) { + map.put("stepX", stepX); + } + if (stepY != null) { + map.put("stepY", stepY); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stepColor", map); + } + + public static ClusterCommand moveToColorTemperature(Integer colorTemperatureMireds, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (colorTemperatureMireds != null) { + map.put("colorTemperatureMireds", colorTemperatureMireds); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToColorTemperature", map); + } + + /** + * This command allows the light to be moved in a smooth continuous transition from their current hue to a target + * hue. + */ + public static ClusterCommand enhancedMoveToHue(Integer enhancedHue, DirectionEnum direction, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (enhancedHue != null) { + map.put("enhancedHue", enhancedHue); + } + if (direction != null) { + map.put("direction", direction); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("enhancedMoveToHue", map); + } + + /** + * This command allows the light to start a continuous transition starting from their current hue. + */ + public static ClusterCommand enhancedMoveHue(MoveModeEnum moveMode, Integer rate, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (moveMode != null) { + map.put("moveMode", moveMode); + } + if (rate != null) { + map.put("rate", rate); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("enhancedMoveHue", map); + } + + /** + * This command allows the light to be moved in a stepped transition from their current hue, resulting in a linear + * transition through XY space. + */ + public static ClusterCommand enhancedStepHue(StepModeEnum stepMode, Integer stepSize, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepMode != null) { + map.put("stepMode", stepMode); + } + if (stepSize != null) { + map.put("stepSize", stepSize); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("enhancedStepHue", map); + } + + /** + * This command allows the light to be moved in a smooth continuous transition from their current hue to a target + * hue and from their current saturation to a target saturation. + */ + public static ClusterCommand enhancedMoveToHueAndSaturation(Integer enhancedHue, Integer saturation, + Integer transitionTime, OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (enhancedHue != null) { + map.put("enhancedHue", enhancedHue); + } + if (saturation != null) { + map.put("saturation", saturation); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("enhancedMoveToHueAndSaturation", map); + } + + /** + * This command allows a color loop to be activated such that the color light cycles through its range of hues. + */ + public static ClusterCommand colorLoopSet(UpdateFlagsBitmap updateFlags, ColorLoopActionEnum action, + ColorLoopDirectionEnum direction, Integer time, Integer startHue, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (updateFlags != null) { + map.put("updateFlags", updateFlags); + } + if (action != null) { + map.put("action", action); + } + if (direction != null) { + map.put("direction", direction); + } + if (time != null) { + map.put("time", time); + } + if (startHue != null) { + map.put("startHue", startHue); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("colorLoopSet", map); + } + + /** + * This command is provided to allow MoveTo and Step commands to be stopped. + */ + public static ClusterCommand stopMoveStep(OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stopMoveStep", map); + } + + /** + * This command allows the color temperature of the light to be moved at a specified rate. + */ + public static ClusterCommand moveColorTemperature(MoveModeEnum moveMode, Integer rate, + Integer colorTemperatureMinimumMireds, Integer colorTemperatureMaximumMireds, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (moveMode != null) { + map.put("moveMode", moveMode); + } + if (rate != null) { + map.put("rate", rate); + } + if (colorTemperatureMinimumMireds != null) { + map.put("colorTemperatureMinimumMireds", colorTemperatureMinimumMireds); + } + if (colorTemperatureMaximumMireds != null) { + map.put("colorTemperatureMaximumMireds", colorTemperatureMaximumMireds); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveColorTemperature", map); + } + + /** + * This command allows the color temperature of the light to be stepped with a specified step size. + */ + public static ClusterCommand stepColorTemperature(StepModeEnum stepMode, Integer stepSize, Integer transitionTime, + Integer colorTemperatureMinimumMireds, Integer colorTemperatureMaximumMireds, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepMode != null) { + map.put("stepMode", stepMode); + } + if (stepSize != null) { + map.put("stepSize", stepSize); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (colorTemperatureMinimumMireds != null) { + map.put("colorTemperatureMinimumMireds", colorTemperatureMinimumMireds); + } + if (colorTemperatureMaximumMireds != null) { + map.put("colorTemperatureMaximumMireds", colorTemperatureMaximumMireds); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stepColorTemperature", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "currentHue : " + currentHue + "\n"; + str += "currentSaturation : " + currentSaturation + "\n"; + str += "remainingTime : " + remainingTime + "\n"; + str += "currentX : " + currentX + "\n"; + str += "currentY : " + currentY + "\n"; + str += "driftCompensation : " + driftCompensation + "\n"; + str += "compensationText : " + compensationText + "\n"; + str += "colorTemperatureMireds : " + colorTemperatureMireds + "\n"; + str += "colorMode : " + colorMode + "\n"; + str += "options : " + options + "\n"; + str += "numberOfPrimaries : " + numberOfPrimaries + "\n"; + str += "primary1X : " + primary1X + "\n"; + str += "primary1Y : " + primary1Y + "\n"; + str += "primary1Intensity : " + primary1Intensity + "\n"; + str += "primary2X : " + primary2X + "\n"; + str += "primary2Y : " + primary2Y + "\n"; + str += "primary2Intensity : " + primary2Intensity + "\n"; + str += "primary3X : " + primary3X + "\n"; + str += "primary3Y : " + primary3Y + "\n"; + str += "primary3Intensity : " + primary3Intensity + "\n"; + str += "primary4X : " + primary4X + "\n"; + str += "primary4Y : " + primary4Y + "\n"; + str += "primary4Intensity : " + primary4Intensity + "\n"; + str += "primary5X : " + primary5X + "\n"; + str += "primary5Y : " + primary5Y + "\n"; + str += "primary5Intensity : " + primary5Intensity + "\n"; + str += "primary6X : " + primary6X + "\n"; + str += "primary6Y : " + primary6Y + "\n"; + str += "primary6Intensity : " + primary6Intensity + "\n"; + str += "whitePointX : " + whitePointX + "\n"; + str += "whitePointY : " + whitePointY + "\n"; + str += "colorPointRx : " + colorPointRx + "\n"; + str += "colorPointRy : " + colorPointRy + "\n"; + str += "colorPointRIntensity : " + colorPointRIntensity + "\n"; + str += "colorPointGx : " + colorPointGx + "\n"; + str += "colorPointGy : " + colorPointGy + "\n"; + str += "colorPointGIntensity : " + colorPointGIntensity + "\n"; + str += "colorPointBx : " + colorPointBx + "\n"; + str += "colorPointBy : " + colorPointBy + "\n"; + str += "colorPointBIntensity : " + colorPointBIntensity + "\n"; + str += "enhancedCurrentHue : " + enhancedCurrentHue + "\n"; + str += "enhancedColorMode : " + enhancedColorMode + "\n"; + str += "colorLoopActive : " + colorLoopActive + "\n"; + str += "colorLoopDirection : " + colorLoopDirection + "\n"; + str += "colorLoopTime : " + colorLoopTime + "\n"; + str += "colorLoopStartEnhancedHue : " + colorLoopStartEnhancedHue + "\n"; + str += "colorLoopStoredEnhancedHue : " + colorLoopStoredEnhancedHue + "\n"; + str += "colorCapabilities : " + colorCapabilities + "\n"; + str += "colorTempPhysicalMinMireds : " + colorTempPhysicalMinMireds + "\n"; + str += "colorTempPhysicalMaxMireds : " + colorTempPhysicalMaxMireds + "\n"; + str += "coupleColorTempToLevelMinMireds : " + coupleColorTempToLevelMinMireds + "\n"; + str += "startUpColorTemperatureMireds : " + startUpColorTemperatureMireds + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CommissionerControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CommissionerControlCluster.java new file mode 100644 index 00000000000..abcd191df20 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/CommissionerControlCluster.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * CommissionerControl + * + * @author Dan Cunningham - Initial contribution + */ +public class CommissionerControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0751; + public static final String CLUSTER_NAME = "CommissionerControl"; + public static final String CLUSTER_PREFIX = "commissionerControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_SUPPORTED_DEVICE_CATEGORIES = "supportedDeviceCategories"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the device categories specified in SupportedDeviceCategoryBitmap that are supported by this + * Commissioner Control Cluster server. + * A client shall NOT send the RequestCommissioningApproval command if the intended node to be commissioned does not + * conform to any of the values specified in SupportedDeviceCategories. + */ + public SupportedDeviceCategoryBitmap supportedDeviceCategories; // 0 SupportedDeviceCategoryBitmap R M + // Structs + + /** + * This event shall be generated by the server following a RequestCommissioningApproval command which the server + * responded to with SUCCESS. + * > [!NOTE] + * > The approval is valid for a period determined by the manufacturer and characteristics of the node presenting + * the Commissioner Control Cluster. Clients SHOULD send the CommissionNode command immediately upon receiving a + * CommissioningRequestResult event. + */ + public class CommissioningRequestResult { + public BigInteger requestId; // uint64 + public BigInteger clientNodeID; // node-id + public Integer statusCode; // status + public Integer fabricIndex; // FabricIndex + + public CommissioningRequestResult(BigInteger requestId, BigInteger clientNodeID, Integer statusCode, + Integer fabricIndex) { + this.requestId = requestId; + this.clientNodeID = clientNodeID; + this.statusCode = statusCode; + this.fabricIndex = fabricIndex; + } + } + + // Bitmaps + public static class SupportedDeviceCategoryBitmap { + /** + * Aggregators which support Fabric Synchronization may be commissioned. + * The FabricSynchronization bit shall be set to 1 if and only if the server supports commissioning nodes that + * support Fabric Synchronization. + */ + public boolean fabricSynchronization; + + public SupportedDeviceCategoryBitmap(boolean fabricSynchronization) { + this.fabricSynchronization = fabricSynchronization; + } + } + + public CommissionerControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1873, "CommissionerControl"); + } + + protected CommissionerControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is sent by a client to request approval for a future CommissionNode call. This is required to be a + * separate step in order to provide the server time for interacting with a user before informing the client that + * the CommissionNode operation may be successful. + * If the command is not executed via a CASE session, the command shall fail with a status code of + * UNSUPPORTED_ACCESS. + * The server may request approval from the user, but it is not required. + * The server shall always return SUCCESS to a correctly formatted RequestCommissioningApproval command, and then + * generate a CommissioningRequestResult event associated with the command’s accessing fabric once the result is + * ready. + * Clients SHOULD avoid using the same RequestID. If the RequestID and client NodeID of a + * RequestCommissioningApproval match a previously received RequestCommissioningApproval and the server has not + * returned an error or completed commissioning of a device for the prior request, then the server SHOULD return + * FAILURE. + * The parameters for RequestCommissioningApproval command are as follows: + */ + public static ClusterCommand requestCommissioningApproval(BigInteger requestId, Integer vendorId, Integer productId, + String label) { + Map map = new LinkedHashMap<>(); + if (requestId != null) { + map.put("requestId", requestId); + } + if (vendorId != null) { + map.put("vendorId", vendorId); + } + if (productId != null) { + map.put("productId", productId); + } + if (label != null) { + map.put("label", label); + } + return new ClusterCommand("requestCommissioningApproval", map); + } + + /** + * This command is sent by a client to request that the server begins commissioning a previously approved request. + * The server shall return FAILURE if the CommissionNode command is not sent from the same NodeID and on the same + * fabric as the RequestCommissioningApproval or if the provided RequestID to CommissionNode does not match the + * value provided to RequestCommissioningApproval. + * If the command is not executed via a CASE session, the command shall fail with a status code of + * UNSUPPORTED_ACCESS. + * Upon receipt, the server shall respond with ReverseOpenCommissioningWindow if CommissioningRequestResult was + * generated with StatusCode of SUCCESS for the matching RequestID field and NodeID of the client. + * The server shall return FAILURE if the CommissionNode command is received after the server has already responded + * to a client with ReverseOpenCommissioningWindow for a matching RequestID field and NodeID of the client unless + * the client has sent another RequestCommissioningApproval and received an additional CommissioningRequestResult. + * The parameters for CommissionNode command are as follows: + */ + public static ClusterCommand commissionNode(BigInteger requestId, Integer responseTimeoutSeconds) { + Map map = new LinkedHashMap<>(); + if (requestId != null) { + map.put("requestId", requestId); + } + if (responseTimeoutSeconds != null) { + map.put("responseTimeoutSeconds", responseTimeoutSeconds); + } + return new ClusterCommand("commissionNode", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "supportedDeviceCategories : " + supportedDeviceCategories + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..0d50b03358a --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ConcentrationMeasurementCluster.java @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class ConcentrationMeasurementCluster extends BaseCluster { + + public static final String CLUSTER_NAME = "ConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "concentrationMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_MEASURED_VALUE = "measuredValue"; + public static final String ATTRIBUTE_MIN_MEASURED_VALUE = "minMeasuredValue"; + public static final String ATTRIBUTE_MAX_MEASURED_VALUE = "maxMeasuredValue"; + public static final String ATTRIBUTE_PEAK_MEASURED_VALUE = "peakMeasuredValue"; + public static final String ATTRIBUTE_PEAK_MEASURED_VALUE_WINDOW = "peakMeasuredValueWindow"; + public static final String ATTRIBUTE_AVERAGE_MEASURED_VALUE = "averageMeasuredValue"; + public static final String ATTRIBUTE_AVERAGE_MEASURED_VALUE_WINDOW = "averageMeasuredValueWindow"; + public static final String ATTRIBUTE_UNCERTAINTY = "uncertainty"; + public static final String ATTRIBUTE_MEASUREMENT_UNIT = "measurementUnit"; + public static final String ATTRIBUTE_MEASUREMENT_MEDIUM = "measurementMedium"; + public static final String ATTRIBUTE_LEVEL_VALUE = "levelValue"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the most recent measurement as a single-precision floating-point number. MeasuredValue’s unit is + * represented by MeasurementUnit. + * A value of null indicates that the measurement is unknown or outside the valid range. MinMeasuredValue and + * MaxMeasuredValue define the valid range for MeasuredValue. + */ + public Float measuredValue; // 0 single R V + /** + * Indicates the minimum value of MeasuredValue that is capable of being measured. A MinMeasuredValue of null + * indicates that the MinMeasuredValue is not defined. + */ + public Float minMeasuredValue; // 1 single R V + /** + * Indicates the maximum value of MeasuredValue that is capable of being measured. A MaxMeasuredValue of null + * indicates that the MaxMeasuredValue is not defined. + */ + public Float maxMeasuredValue; // 2 single R V + /** + * Indicates the maximum value of MeasuredValue that has been measured during the PeakMeasuredValueWindow. If this + * attribute is provided, the PeakMeasuredValueWindow attribute shall also be provided. + */ + public Float peakMeasuredValue; // 3 single R V + /** + * Indicates the window of time used for determining the PeakMeasuredValue. The value is in seconds. + */ + public Integer peakMeasuredValueWindow; // 4 elapsed-s R V + /** + * Indicates the average value of MeasuredValue that has been measured during the AverageMeasuredValueWindow. If + * this attribute is provided, the AverageMeasuredValueWindow attribute shall also be provided. + */ + public Float averageMeasuredValue; // 5 single R V + /** + * This attribute shall represent the window of time used for determining the AverageMeasuredValue. The value is in + * seconds. + */ + public Integer averageMeasuredValueWindow; // 6 elapsed-s R V + /** + * Indicates the range of error or deviation that can be found in MeasuredValue and PeakMeasuredValue. This is + * considered a +/- value and should be considered to be in MeasurementUnit. + */ + public Float uncertainty; // 7 single R V + /** + * Indicates the unit of MeasuredValue. See MeasurementUnitEnum. + */ + public MeasurementUnitEnum measurementUnit; // 8 MeasurementUnitEnum R V + /** + * Indicates the medium in which MeasuredValue is being measured. See MeasurementMediumEnum. + */ + public MeasurementMediumEnum measurementMedium; // 9 MeasurementMediumEnum R V + /** + * Indicates the level of the substance detected. See LevelValueEnum. + */ + public LevelValueEnum levelValue; // 10 LevelValueEnum R V + + // Enums + /** + * Where mentioned, Billion refers to 10, Trillion refers to 1012 (short scale). + */ + public enum MeasurementUnitEnum implements MatterEnum { + PPM(0, "Ppm"), + PPB(1, "Ppb"), + PPT(2, "Ppt"), + MGM3(3, "Mgm 3"), + UGM3(4, "Ugm 3"), + NGM3(5, "Ngm 3"), + PM3(6, "Pm 3"), + BQM3(7, "Bqm 3"); + + public final Integer value; + public final String label; + + private MeasurementUnitEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum MeasurementMediumEnum implements MatterEnum { + AIR(0, "Air"), + WATER(1, "Water"), + SOIL(2, "Soil"); + + public final Integer value; + public final String label; + + private MeasurementMediumEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum LevelValueEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + LOW(1, "Low"), + MEDIUM(2, "Medium"), + HIGH(3, "High"), + CRITICAL(4, "Critical"); + + public final Integer value; + public final String label; + + private LevelValueEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Cluster supports numeric measurement of substance + */ + public boolean numericMeasurement; + /** + * + * Cluster supports basic level indication for substance using the ConcentrationLev el enum + */ + public boolean levelIndication; + /** + * + * Cluster supports the Medium Concentration Level + */ + public boolean mediumLevel; + /** + * + * Cluster supports the Critical Concentration Level + */ + public boolean criticalLevel; + /** + * + * Cluster supports peak numeric measurement of substance + */ + public boolean peakMeasurement; + /** + * + * Cluster supports average numeric measurement of substance + */ + public boolean averageMeasurement; + + public FeatureMap(boolean numericMeasurement, boolean levelIndication, boolean mediumLevel, + boolean criticalLevel, boolean peakMeasurement, boolean averageMeasurement) { + this.numericMeasurement = numericMeasurement; + this.levelIndication = levelIndication; + this.mediumLevel = mediumLevel; + this.criticalLevel = criticalLevel; + this.peakMeasurement = peakMeasurement; + this.averageMeasurement = averageMeasurement; + } + } + + protected ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "measuredValue : " + measuredValue + "\n"; + str += "minMeasuredValue : " + minMeasuredValue + "\n"; + str += "maxMeasuredValue : " + maxMeasuredValue + "\n"; + str += "peakMeasuredValue : " + peakMeasuredValue + "\n"; + str += "peakMeasuredValueWindow : " + peakMeasuredValueWindow + "\n"; + str += "averageMeasuredValue : " + averageMeasuredValue + "\n"; + str += "averageMeasuredValueWindow : " + averageMeasuredValueWindow + "\n"; + str += "uncertainty : " + uncertainty + "\n"; + str += "measurementUnit : " + measurementUnit + "\n"; + str += "measurementMedium : " + measurementMedium + "\n"; + str += "levelValue : " + levelValue + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentAppObserverCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentAppObserverCluster.java new file mode 100644 index 00000000000..9685a6bd9e1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentAppObserverCluster.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ContentAppObserver + * + * @author Dan Cunningham - Initial contribution + */ +public class ContentAppObserverCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0510; + public static final String CLUSTER_NAME = "ContentAppObserver"; + public static final String CLUSTER_PREFIX = "contentAppObserver"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + + // Enums + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + UNEXPECTED_DATA(1, "Unexpected Data"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public ContentAppObserverCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1296, "ContentAppObserver"); + } + + protected ContentAppObserverCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, the data field may be parsed and interpreted. Message encoding is specific to the Content App. A + * Content App may when possible read attributes from the Basic Information Cluster on the Observer and use this to + * determine the Message encoding. + * This command returns a ContentAppMessage Response. + */ + public static ClusterCommand contentAppMessage(String data, String encodingHint) { + Map map = new LinkedHashMap<>(); + if (data != null) { + map.put("data", data); + } + if (encodingHint != null) { + map.put("encodingHint", encodingHint); + } + return new ClusterCommand("contentAppMessage", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentControlCluster.java new file mode 100644 index 00000000000..3c3087fa8c9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentControlCluster.java @@ -0,0 +1,655 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ContentControl + * + * @author Dan Cunningham - Initial contribution + */ +public class ContentControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x050F; + public static final String CLUSTER_NAME = "ContentControl"; + public static final String CLUSTER_PREFIX = "contentControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ENABLED = "enabled"; + public static final String ATTRIBUTE_ON_DEMAND_RATINGS = "onDemandRatings"; + public static final String ATTRIBUTE_ON_DEMAND_RATING_THRESHOLD = "onDemandRatingThreshold"; + public static final String ATTRIBUTE_SCHEDULED_CONTENT_RATINGS = "scheduledContentRatings"; + public static final String ATTRIBUTE_SCHEDULED_CONTENT_RATING_THRESHOLD = "scheduledContentRatingThreshold"; + public static final String ATTRIBUTE_SCREEN_DAILY_TIME = "screenDailyTime"; + public static final String ATTRIBUTE_REMAINING_SCREEN_TIME = "remainingScreenTime"; + public static final String ATTRIBUTE_BLOCK_UNRATED = "blockUnrated"; + public static final String ATTRIBUTE_BLOCK_CHANNEL_LIST = "blockChannelList"; + public static final String ATTRIBUTE_BLOCK_APPLICATION_LIST = "blockApplicationList"; + public static final String ATTRIBUTE_BLOCK_CONTENT_TIME_WINDOW = "blockContentTimeWindow"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates whether the Content Control feature implemented on a media device is turned off (FALSE) or turned on + * (TRUE). + */ + public Boolean enabled; // 0 bool R V + /** + * This attribute shall provide the collection of ratings that are currently valid for this media device. The items + * should honor the metadata of the on-demand content (e.g. Movie) rating system for one country or region where the + * media device has been provisioned. For example, for the MPAA system, RatingName may be one value out of + * "G", "PG", "PG-13", "R", "NC-17". + * The media device shall have a way to determine which rating system applies for the on-demand content and then + * populate this attribute. For example, it can do it through examining the Location attribute in the Basic + * Information cluster, and then determining which rating system applies. + * The ratings in this collection shall be in order from a rating for the youngest viewers to the one for the oldest + * viewers. Each rating in the list shall be unique. + */ + public List onDemandRatings; // 1 list R V + /** + * Indicates a threshold rating as a content filter which is compared with the rating for on-demand content. For + * example, if the on-demand content rating is greater than or equal to OnDemandRatingThreshold, for a rating system + * that is ordered from lower viewer age to higher viewer age, then on-demand content is not appropriate for the + * User and the Node shall prevent the playback of content. + * This attribute shall be set to one of the values present in the OnDemandRatings attribute. + * When this attribute changes, the device SHOULD make the user aware of any limits of this feature. For example, if + * the feature does not control content within apps, then the device should make this clear to the user when the + * attribute changes. + */ + public String onDemandRatingThreshold; // 2 string R V + /** + * Indicates a collection of ratings which ScheduledContentRatingThreshold can be set to. The items should honor + * metadata of the scheduled content rating system for the country or region where the media device has been + * provisioned. + * The media device shall have a way to determine which scheduled content rating system applies and then populate + * this attribute. For example, this can be done by examining the Location attribute in Basic Information cluster, + * and then determining which rating system applies. + * The ratings in this collection shall be in order from a rating for the youngest viewers to the one for the oldest + * viewers. Each rating in the list shall be unique. + */ + public List scheduledContentRatings; // 3 list R V + /** + * Indicates a threshold rating as a content filter which is used to compare with the rating for scheduled content. + * For example, if the scheduled content rating is greater than or equal to ScheduledContentRatingThreshold for a + * rating system that is ordered from lower viewer age to higher viewer age, then the scheduled content is not + * appropriate for the User and shall be blocked. + * This attribute shall be set to one of the values present in the ScheduledContentRatings attribute. + * When this attribute changes, the device SHOULD make the user aware of any limits of this feature. For example, if + * the feature does not control content within apps, then the device should make this clear to the user when the + * attribute changes. + */ + public String scheduledContentRatingThreshold; // 4 string R V + /** + * Indicates the amount of time (in seconds) which the User is allowed to spend watching TV within one day when the + * Content Control feature is activated. + */ + public Integer screenDailyTime; // 5 elapsed-s R V + /** + * Indicates the remaining screen time (in seconds) which the User is allowed to spend watching TV for the current + * day when the Content Control feature is activated. When this value equals 0, the media device shall terminate the + * playback of content. + * This attribute shall be updated when the AddBonusTime command is received and processed successfully (with the + * correct PIN). + */ + public Integer remainingScreenTime; // 6 elapsed-s R V + /** + * Indicates whether the playback of unrated content is allowed when the Content Control feature is activated. If + * this attribute equals FALSE, then playback of unrated content shall be permitted. Otherwise, the media device + * shall prevent the playback of unrated content. + * When this attribute changes, the device SHOULD make the user aware of any limits of this feature. + * For example, if the feature does not control content within apps, then the device should make this clear to the + * user when the attribute changes. + */ + public Boolean blockUnrated; // 7 bool R V + /** + * Indicates a set of channels that shall be blocked when the Content Control feature is activated. + */ + public List blockChannelList; // 8 list R V + /** + * Indicates a set of applications that shall be blocked when the Content Control feature is activated. + */ + public List blockApplicationList; // 9 list R V + /** + * Indicates a set of periods during which the playback of content on media device shall be blocked when the Content + * Control feature is activated. The media device shall reject any request to play content during one period of this + * attribute. If it is entering any one period of this attribute, the media device shall block content which is + * playing and generate an event EnteringBlockContentTimeWindow. There shall NOT be multiple entries in this + * attribute list for the same day of week. + */ + public List blockContentTimeWindow; // 10 list R V + // Structs + + /** + * This event shall be generated when the RemainingScreenTime equals 0. + */ + public class RemainingScreenTimeExpired { + public RemainingScreenTimeExpired() { + } + } + + /** + * This event shall be generated when entering a period of blocked content as configured in the + * BlockContentTimeWindow attribute. + */ + public class EnteringBlockContentTimeWindow { + public EnteringBlockContentTimeWindow() { + } + } + + public class RatingNameStruct { + /** + * This field shall indicate the name of the rating level of the applied rating system. The applied rating + * system is dependent upon the region or country where the Node has been provisioned, and may vary from one + * country to another. + */ + public String ratingName; // string + /** + * This field shall specify a human readable (displayable) description for RatingName. + */ + public String ratingNameDesc; // string + + public RatingNameStruct(String ratingName, String ratingNameDesc) { + this.ratingName = ratingName; + this.ratingNameDesc = ratingNameDesc; + } + } + + public class BlockChannelStruct { + /** + * This field shall indicate a unique index value for a blocked channel. This value may be used to indicate one + * selected channel which will be removed from BlockChannelList attribute. + */ + public Integer blockChannelIndex; // uint16 + /** + * This field shall indicate the channel major number value (for example, using ATSC format). When the channel + * number is expressed as a string, such as "13.1" or "256", the major number would be 13 or + * 256, respectively. This field is required but shall be set to 0 for channels such as over-the-top channels + * that are not represented by a major or minor number. + */ + public Integer majorNumber; // uint16 + /** + * This field shall indicate the channel minor number value (for example, using ATSC format). When the channel + * number is expressed as a string, such as "13.1" or "256", the minor number would be 1 or + * 0, respectively. This field is required but shall be set to 0 for channels such as over-the-top channels that + * are not represented by a major or minor number. + */ + public Integer minorNumber; // uint16 + /** + * This field shall indicate the unique identifier for a specific channel. This field is optional, but SHOULD be + * provided when MajorNumber and MinorNumber are not available. + */ + public String identifier; // string + + public BlockChannelStruct(Integer blockChannelIndex, Integer majorNumber, Integer minorNumber, + String identifier) { + this.blockChannelIndex = blockChannelIndex; + this.majorNumber = majorNumber; + this.minorNumber = minorNumber; + this.identifier = identifier; + } + } + + public class AppInfoStruct { + /** + * This field shall indicate the CSA-issued vendor ID for the catalog. The DIAL registry shall use value 0x0000. + * Content App Platform providers will have their own catalog vendor ID (set to their own Vendor ID) and will + * assign an ApplicationID to each Content App. + */ + public Integer catalogVendorId; // uint16 + /** + * This field shall indicate the application identifier, expressed as a string, such as "PruneVideo" + * or "Company X". This field shall be unique within a catalog. + */ + public String applicationId; // string + + public AppInfoStruct(Integer catalogVendorId, String applicationId) { + this.catalogVendorId = catalogVendorId; + this.applicationId = applicationId; + } + } + + public class TimeWindowStruct { + /** + * This field shall indicate a unique index of a specific time window. This value may be used to indicate a + * selected time window which will be removed from the BlockContentTimeWindow attribute. + */ + public Integer timeWindowIndex; // uint16 + /** + * This field shall indicate a day of week. + */ + public DayOfWeekBitmap dayOfWeek; // DayOfWeekBitmap + /** + * This field shall indicate one or more discrete time periods. + */ + public List timePeriod; // list + + public TimeWindowStruct(Integer timeWindowIndex, DayOfWeekBitmap dayOfWeek, List timePeriod) { + this.timeWindowIndex = timeWindowIndex; + this.dayOfWeek = dayOfWeek; + this.timePeriod = timePeriod; + } + } + + public class TimePeriodStruct { + /** + * This field shall indicate the starting hour. + */ + public Integer startHour; // uint8 + /** + * This field shall indicate the starting minute. + */ + public Integer startMinute; // uint8 + /** + * This field shall indicate the ending hour. EndHour shall be equal to or greater than StartHour + */ + public Integer endHour; // uint8 + /** + * This field shall indicate the ending minute. If EndHour is equal to StartHour then EndMinute shall be greater + * than StartMinute. If the EndHour is equal to 23 and the EndMinute is equal to 59, all contents shall be + * blocked until 23:59:59. + */ + public Integer endMinute; // uint8 + + public TimePeriodStruct(Integer startHour, Integer startMinute, Integer endHour, Integer endMinute) { + this.startHour = startHour; + this.startMinute = startMinute; + this.endHour = endHour; + this.endMinute = endMinute; + } + } + + // Enums + public enum StatusCodeEnum implements MatterEnum { + INVALID_PIN_CODE(2, "Invalid Pin Code"), + INVALID_RATING(3, "Invalid Rating"), + INVALID_CHANNEL(4, "Invalid Channel"), + CHANNEL_ALREADY_EXIST(5, "Channel Already Exist"), + CHANNEL_NOT_EXIST(6, "Channel Not Exist"), + UNIDENTIFIABLE_APPLICATION(7, "Unidentifiable Application"), + APPLICATION_ALREADY_EXIST(8, "Application Already Exist"), + APPLICATION_NOT_EXIST(9, "Application Not Exist"), + TIME_WINDOW_ALREADY_EXIST(10, "Time Window Already Exist"), + TIME_WINDOW_NOT_EXIST(11, "Time Window Not Exist"); + + public final Integer value; + public final String label; + + private StatusCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class DayOfWeekBitmap { + public boolean sunday; + public boolean monday; + public boolean tuesday; + public boolean wednesday; + public boolean thursday; + public boolean friday; + public boolean saturday; + + public DayOfWeekBitmap(boolean sunday, boolean monday, boolean tuesday, boolean wednesday, boolean thursday, + boolean friday, boolean saturday) { + this.sunday = sunday; + this.monday = monday; + this.tuesday = tuesday; + this.wednesday = wednesday; + this.thursday = thursday; + this.friday = friday; + this.saturday = saturday; + } + } + + public static class FeatureMap { + /** + * + * Supports managing screen time limits. + */ + public boolean screenTime; + /** + * + * Supports managing a PIN code which is used for restricting access to configuration of this feature. + */ + public boolean pinManagement; + /** + * + * Supports managing content controls for unrated content. + */ + public boolean blockUnrated; + /** + * + * Supports managing content controls based upon rating threshold for on demand content. + */ + public boolean onDemandContentRating; + /** + * + * Supports managing content controls based upon rating threshold for scheduled content. + */ + public boolean scheduledContentRating; + /** + * + * Supports managing a set of channels that are prohibited. + */ + public boolean blockChannels; + /** + * + * Supports managing a set of applications that are prohibited. + */ + public boolean blockApplications; + /** + * + * Supports managing content controls based upon setting time window in which all contents and applications + * SHALL be blocked. + */ + public boolean blockContentTimeWindow; + + public FeatureMap(boolean screenTime, boolean pinManagement, boolean blockUnrated, + boolean onDemandContentRating, boolean scheduledContentRating, boolean blockChannels, + boolean blockApplications, boolean blockContentTimeWindow) { + this.screenTime = screenTime; + this.pinManagement = pinManagement; + this.blockUnrated = blockUnrated; + this.onDemandContentRating = onDemandContentRating; + this.scheduledContentRating = scheduledContentRating; + this.blockChannels = blockChannels; + this.blockApplications = blockApplications; + this.blockContentTimeWindow = blockContentTimeWindow; + } + } + + public ContentControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1295, "ContentControl"); + } + + protected ContentControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * The purpose of this command is to update the PIN used for protecting configuration of the content control + * settings. Upon success, the old PIN shall no longer work. + * The PIN is used to ensure that only the Node (or User) with the PIN code can make changes to the Content Control + * settings, for example, turn off Content Controls or modify the ScreenDailyTime. The PIN is composed of a numeric + * string of up to 6 human readable characters (displayable) . + * Upon receipt of this command, the media device shall check if the OldPIN field of this command is the same as the + * current PIN. If the PINs are the same, then the PIN code shall be set to NewPIN. Otherwise a response with + * InvalidPINCode error status shall be returned. + * The media device may provide a default PIN to the User via an out of band mechanism. For security reasons, it is + * recommended that a client encourage the user to update the PIN from its default value when performing + * configuration of the Content Control settings exposed by this cluster. The ResetPIN command can also be used to + * obtain the default PIN. + */ + public static ClusterCommand updatePin(String oldPin, String newPin) { + Map map = new LinkedHashMap<>(); + if (oldPin != null) { + map.put("oldPin", oldPin); + } + if (newPin != null) { + map.put("newPin", newPin); + } + return new ClusterCommand("updatePin", map); + } + + /** + * The purpose of this command is to reset the PIN. + * If this command is executed successfully, a ResetPINResponse command with a new PIN shall be returned. + */ + public static ClusterCommand resetPin() { + return new ClusterCommand("resetPin"); + } + + /** + * The purpose of this command is to turn on the Content Control feature on a media device. + * Upon receipt of the Enable command, the media device shall set the Enabled attribute to TRUE. + */ + public static ClusterCommand enable() { + return new ClusterCommand("enable"); + } + + /** + * The purpose of this command is to turn off the Content Control feature on a media device. + * On receipt of the Disable command, the media device shall set the Enabled attribute to FALSE. + */ + public static ClusterCommand disable() { + return new ClusterCommand("disable"); + } + + /** + * The purpose of this command is to add the extra screen time for the user. + * If a client with Operate privilege invokes this command, the media device shall check whether the PINCode passed + * in the command matches the current PINCode value. If these match, then the RemainingScreenTime attribute shall be + * increased by the specified BonusTime value. + * If the PINs do not match, then a response with InvalidPINCode error status shall be returned, and no changes + * shall be made to RemainingScreenTime. + * If a client with Manage privilege or greater invokes this command, the media device shall ignore the PINCode + * field and directly increase the RemainingScreenTime attribute by the specified BonusTime value. + * A server that does not support the PM feature shall respond with InvalidPINCode to clients that only have Operate + * privilege unless: + * • It has been provided with the PIN value to expect via an out of band mechanism, and + * • The client has provided a PINCode that matches the expected PIN value. + */ + public static ClusterCommand addBonusTime(String pinCode, Integer bonusTime) { + Map map = new LinkedHashMap<>(); + if (pinCode != null) { + map.put("pinCode", pinCode); + } + if (bonusTime != null) { + map.put("bonusTime", bonusTime); + } + return new ClusterCommand("addBonusTime", map); + } + + /** + * The purpose of this command is to set the ScreenDailyTime attribute. + * Upon receipt of the SetScreenDailyTime command, the media device shall set the ScreenDailyTime attribute to the + * ScreenTime value. + */ + public static ClusterCommand setScreenDailyTime(Integer screenTime) { + Map map = new LinkedHashMap<>(); + if (screenTime != null) { + map.put("screenTime", screenTime); + } + return new ClusterCommand("setScreenDailyTime", map); + } + + /** + * The purpose of this command is to specify whether programs with no Content rating must be blocked by this media + * device. + * Upon receipt of the BlockUnratedContent command, the media device shall set the BlockUnrated attribute to TRUE. + */ + public static ClusterCommand blockUnratedContent() { + return new ClusterCommand("blockUnratedContent"); + } + + /** + * The purpose of this command is to specify whether programs with no Content rating must be blocked by this media + * device. + * Upon receipt of the UnblockUnratedContent command, the media device shall set the BlockUnrated attribute to + * FALSE. + */ + public static ClusterCommand unblockUnratedContent() { + return new ClusterCommand("unblockUnratedContent"); + } + + /** + * The purpose of this command is to set the OnDemandRatingThreshold attribute. + * Upon receipt of the SetOnDemandRatingThreshold command, the media device shall check if the Rating field is one + * of values present in the OnDemandRatings attribute. If not, then a response with InvalidRating error status shall + * be returned. + */ + public static ClusterCommand setOnDemandRatingThreshold(String rating) { + Map map = new LinkedHashMap<>(); + if (rating != null) { + map.put("rating", rating); + } + return new ClusterCommand("setOnDemandRatingThreshold", map); + } + + /** + * The purpose of this command is to set ScheduledContentRatingThreshold attribute. + * Upon receipt of the SetScheduledContentRatingThreshold command, the media device shall check if the Rating field + * is one of values present in the ScheduledContentRatings attribute. If not, then a response with InvalidRating + * error status shall be returned. + */ + public static ClusterCommand setScheduledContentRatingThreshold(String rating) { + Map map = new LinkedHashMap<>(); + if (rating != null) { + map.put("rating", rating); + } + return new ClusterCommand("setScheduledContentRatingThreshold", map); + } + + /** + * The purpose of this command is to set BlockChannelList attribute. + * Upon receipt of the AddBlockChannels command, the media device shall check if the channels passed in this command + * are valid. If the channel is invalid, then a response with InvalidChannel error Status shall be returned. + * If there is at least one channel in Channels field which is not in the BlockChannelList attribute, the media + * device shall process the request by adding these new channels into the BlockChannelList attribute and return a + * successful Status Response. During this process, the media device shall assign one unique index to + * BlockChannelIndex field for every channel passed in this command. + * If all channels in Channel field already exist in the BlockChannelList attribute, then a response with + * ChannelAlreadyExist error Status shall be returned. + */ + public static ClusterCommand addBlockChannels(List channels) { + Map map = new LinkedHashMap<>(); + if (channels != null) { + map.put("channels", channels); + } + return new ClusterCommand("addBlockChannels", map); + } + + /** + * The purpose of this command is to remove channels from the BlockChannelList attribute. + * Upon receipt of the RemoveBlockChannels command, the media device shall check if the channels indicated by + * ChannelIndexes passed in this command are present in BlockChannelList attribute. If one or more channels + * indicated by ChannelIndexes passed in this command field are not present in the BlockChannelList attribute, then + * a response with ChannelNotExist error Status shall be returned. + */ + public static ClusterCommand removeBlockChannels(List channelIndexes) { + Map map = new LinkedHashMap<>(); + if (channelIndexes != null) { + map.put("channelIndexes", channelIndexes); + } + return new ClusterCommand("removeBlockChannels", map); + } + + /** + * The purpose of this command is to set applications to the BlockApplicationList attribute. + * Upon receipt of the AddBlockApplications command, the media device shall check if the Applications passed in this + * command are installed. If there is an application in Applications field which is not identified by media device, + * then a response with UnidentifiableApplication error Status may be returned. + * If there is one or more applications which are not present in BlockApplicationList attribute, the media device + * shall process the request by adding the new application to the BlockApplicationList attribute and return a + * successful Status Response. + * If all applications in Applications field are already present in BlockApplicationList attribute, then a response + * with ApplicationAlreadyExist error Status shall be returned. + */ + public static ClusterCommand addBlockApplications(List applications) { + Map map = new LinkedHashMap<>(); + if (applications != null) { + map.put("applications", applications); + } + return new ClusterCommand("addBlockApplications", map); + } + + /** + * The purpose of this command is to remove applications from the BlockApplicationList attribute. + * Upon receipt of the RemoveBlockApplications command, the media device shall check if the applications passed in + * this command present in the BlockApplicationList attribute. If one or more applications in Applications field + * which are not present in the BlockApplicationList attribute, then a response with ApplicationNotExist error + * Status shall be returned. + */ + public static ClusterCommand removeBlockApplications(List applications) { + Map map = new LinkedHashMap<>(); + if (applications != null) { + map.put("applications", applications); + } + return new ClusterCommand("removeBlockApplications", map); + } + + /** + * The purpose of this command is to set the BlockContentTimeWindow attribute. + * Upon receipt of the SetBlockContentTimeWindow command, the media device shall check if the TimeWindowIndex field + * passed in this command is NULL. If the TimeWindowIndex field is NULL, the media device shall check if there is an + * entry in the BlockContentTimeWindow attribute which matches with the TimePeriod and DayOfWeek fields passed in + * this command. * If Yes, then a response with TimeWindowAlreadyExist error status shall be returned. * If No, then + * the media device shall assign one unique index for this time window and add it into the BlockContentTimeWindow + * list attribute. + * If the TimeWindowIndex field is not NULL and presents in the BlockContentTimeWindow attribute, the media device + * shall replace the original time window with the new time window passed in this command. + */ + public static ClusterCommand setBlockContentTimeWindow(TimeWindowStruct timeWindow) { + Map map = new LinkedHashMap<>(); + if (timeWindow != null) { + map.put("timeWindow", timeWindow); + } + return new ClusterCommand("setBlockContentTimeWindow", map); + } + + /** + * The purpose of this command is to remove the selected time windows from the BlockContentTimeWindow attribute. + * Upon receipt of the RemoveBlockContentTimeWindow command, the media device shall check if the time window index + * passed in this command presents in the BlockContentTimeWindow attribute. + * If one or more time window indexes passed in this command are not present in BlockContentTimeWindow attribute, + * then a response with TimeWindowNotExist error status shall be returned. + */ + public static ClusterCommand removeBlockContentTimeWindow(List timeWindowIndexes) { + Map map = new LinkedHashMap<>(); + if (timeWindowIndexes != null) { + map.put("timeWindowIndexes", timeWindowIndexes); + } + return new ClusterCommand("removeBlockContentTimeWindow", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "enabled : " + enabled + "\n"; + str += "onDemandRatings : " + onDemandRatings + "\n"; + str += "onDemandRatingThreshold : " + onDemandRatingThreshold + "\n"; + str += "scheduledContentRatings : " + scheduledContentRatings + "\n"; + str += "scheduledContentRatingThreshold : " + scheduledContentRatingThreshold + "\n"; + str += "screenDailyTime : " + screenDailyTime + "\n"; + str += "remainingScreenTime : " + remainingScreenTime + "\n"; + str += "blockUnrated : " + blockUnrated + "\n"; + str += "blockChannelList : " + blockChannelList + "\n"; + str += "blockApplicationList : " + blockApplicationList + "\n"; + str += "blockContentTimeWindow : " + blockContentTimeWindow + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentLauncherCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentLauncherCluster.java new file mode 100644 index 00000000000..2c266ad541b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ContentLauncherCluster.java @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ContentLauncher + * + * @author Dan Cunningham - Initial contribution + */ +public class ContentLauncherCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x050A; + public static final String CLUSTER_NAME = "ContentLauncher"; + public static final String CLUSTER_PREFIX = "contentLauncher"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ACCEPT_HEADER = "acceptHeader"; + public static final String ATTRIBUTE_SUPPORTED_STREAMING_PROTOCOLS = "supportedStreamingProtocols"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall provide a list of content types supported by the Video Player or Content App in the form of + * entries in the HTTP "Accept" request header. + */ + public List acceptHeader; // 0 list R V + /** + * This attribute shall provide information about supported streaming protocols. + */ + public SupportedProtocolsBitmap supportedStreamingProtocols; // 1 SupportedProtocolsBitmap R V + // Structs + + /** + * This object defines additional name=value pairs that can be used for identifying content. + */ + public class AdditionalInfoStruct { + /** + * This field shall indicate the name of external id, ex. "musicbrainz". + */ + public String name; // string + /** + * This field shall indicate the value for external id, ex. "ST0000000666661". + */ + public String value; // string + + public AdditionalInfoStruct(String name, String value) { + this.name = name; + this.value = value; + } + } + + /** + * This object defines inputs to a search for content for display or playback. + */ + public class ParameterStruct { + /** + * This field shall indicate the entity type. + */ + public ParameterEnum type; // ParameterEnum + /** + * This field shall indicate the entity value, which is a search string, ex. “Manchester by the Sea”. + */ + public String value; // string + /** + * This field shall indicate the list of additional external content identifiers. + */ + public List externalIdList; // list + + public ParameterStruct(ParameterEnum type, String value, List externalIdList) { + this.type = type; + this.value = value; + this.externalIdList = externalIdList; + } + } + + /** + * This object defines inputs to a search for content for display or playback. + */ + public class ContentSearchStruct { + /** + * This field shall indicate the list of parameters comprising the search. If multiple parameters are provided, + * the search parameters shall be joined with 'AND' logic. e.g. action movies with Tom Cruise will be + * represented as [{Actor: 'Tom Cruise'}, {Type: 'Movie'}, {Genre: 'Action'}] + */ + public List parameterList; // list + + public ContentSearchStruct(List parameterList) { + this.parameterList = parameterList; + } + } + + /** + * This object defines dimension which can be used for defining Size of background images. + */ + public class DimensionStruct { + /** + * This field shall indicate the width using the metric defined in Metric + */ + public Double width; // double + /** + * This field shall indicate the height using the metric defined in Metric + */ + public Double height; // double + /** + * This field shall indicate metric used for defining Height/Width. + */ + public MetricTypeEnum metric; // MetricTypeEnum + + public DimensionStruct(Double width, Double height, MetricTypeEnum metric) { + this.width = width; + this.height = height; + this.metric = metric; + } + } + + /** + * This object defines style information which can be used by content providers to change the Media Player’s style + * related properties. + */ + public class StyleInformationStruct { + /** + * This field shall indicate the URL of image used for Styling different Video Player sections like Logo, + * Watermark etc. The syntax of this field shall follow the syntax as specified in RFC 1738 and shall use the + * https scheme. + */ + public String imageUrl; // string + /** + * This field shall indicate the color, in RGB or RGBA, used for styling different Video Player sections like + * Logo, Watermark, etc. The value shall conform to the 6-digit or 8-digit format defined for CSS sRGB + * hexadecimal color notation. Examples: + * • #76DE19 for R=0x76, G=0xDE, B=0x19, A absent + * • #76DE1980 for R=0x76, G=0xDE, B=0x19, A=0x80 + */ + public String color; // string + /** + * This field shall indicate the size of the image used for Styling different Video Player sections like Logo, + * Watermark etc. + */ + public DimensionStruct size; // DimensionStruct + + public StyleInformationStruct(String imageUrl, String color, DimensionStruct size) { + this.imageUrl = imageUrl; + this.color = color; + this.size = size; + } + } + + /** + * This object defines Branding Information which can be provided by the client in order to customize the skin of + * the Video Player during playback. + */ + public class BrandingInformationStruct { + /** + * This field shall indicate name of the provider for the given content. + */ + public String providerName; // string + /** + * This field shall indicate background of the Video Player while content launch request is being processed by + * it. This background information may also be used by the Video Player when it is in idle state. + */ + public StyleInformationStruct background; // StyleInformationStruct + /** + * This field shall indicate the logo shown when the Video Player is launching. This is also used when the Video + * Player is in the idle state and Splash field is not available. + */ + public StyleInformationStruct logo; // StyleInformationStruct + /** + * This field shall indicate the style of progress bar for media playback. + */ + public StyleInformationStruct progressBar; // StyleInformationStruct + /** + * This field shall indicate the screen shown when the Video Player is in an idle state. If this property is not + * populated, the Video Player shall default to logo or the provider name. + */ + public StyleInformationStruct splash; // StyleInformationStruct + /** + * This field shall indicate watermark shown when the media is playing. + */ + public StyleInformationStruct watermark; // StyleInformationStruct + + public BrandingInformationStruct(String providerName, StyleInformationStruct background, + StyleInformationStruct logo, StyleInformationStruct progressBar, StyleInformationStruct splash, + StyleInformationStruct watermark) { + this.providerName = providerName; + this.background = background; + this.logo = logo; + this.progressBar = progressBar; + this.splash = splash; + this.watermark = watermark; + } + } + + /** + * PlaybackPreferencesStruct defines the preferences sent by the client to the receiver in the ContentLauncher + * LaunchURL or LaunchContent commands. + */ + public class PlaybackPreferencesStruct { + /** + * This field shall indicate the preferred position (in milliseconds) in the media to launch playback from. In + * case the position falls in the middle of a frame, the server shall set the position to the beginning of that + * frame and set the SampledPosition attribute on the MediaPlayback cluster accordingly. A value of null shall + * indicate that playback position is not applicable for the current state of the media playback. (For example : + * Live media with no known duration and where seek is not supported). + */ + public BigInteger playbackPosition; // uint64 + /** + * This field shall indicate the user’s preferred Text Track. A value of null shall indicate that the user did + * not specify a preferred Text Track on the client. In such a case, the decision to display and select a Text + * Track is up to the server. + */ + public TrackPreferenceStruct textTrack; // TrackPreferenceStruct + /** + * This field shall indicate the list of the user’s preferred Audio Tracks. If the list contains multiple + * values, each AudioTrack must also specify a unique audioOutputIndex to play the track on. A value of null + * shall indicate that the user did not specify a preferred Audio Track on the client. In such a case, the + * decision to play and select an Audio Track is up to the server. + */ + public List audioTracks; // list + + public PlaybackPreferencesStruct(BigInteger playbackPosition, TrackPreferenceStruct textTrack, + List audioTracks) { + this.playbackPosition = playbackPosition; + this.textTrack = textTrack; + this.audioTracks = audioTracks; + } + } + + /** + * This structure defines Text/Audio Track preferences. + */ + public class TrackPreferenceStruct { + /** + * This field shall contain one of the standard Tags for Identifying Languages RFC 5646, which identifies the + * primary language used in the Track. + */ + public String languageCode; // string + /** + * This field shall contain a list of enumerated CharacteristicEnum values that indicate a purpose, trait or + * feature associated with the Track. A value of null shall indicate that there are no Characteristics + * corresponding to the Track. + */ + public List characteristics; // list + /** + * This field if present shall indicate the index of the OutputInfoStruct from the OutputList attribute (from + * the AudioOutput cluster) and indicates which audio output the Audio Track should be played on. + * This field shall NOT be present if the track is not an audio track. + * If the track is an audio track, this field MUST be present. A value of null shall indicate that the server + * can choose the audio output(s) to play the Audio Track on. + */ + public Integer audioOutputIndex; // uint8 + + public TrackPreferenceStruct(String languageCode, List characteristics, + Integer audioOutputIndex) { + this.languageCode = languageCode; + this.characteristics = characteristics; + this.audioOutputIndex = audioOutputIndex; + } + } + + // Enums + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + URL_NOT_AVAILABLE(1, "Url Not Available"), + AUTH_FAILED(2, "Auth Failed"), + TEXT_TRACK_NOT_AVAILABLE(3, "Text Track Not Available"), + AUDIO_TRACK_NOT_AVAILABLE(4, "Audio Track Not Available"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ParameterEnum implements MatterEnum { + ACTOR(0, "Actor"), + CHANNEL(1, "Channel"), + CHARACTER(2, "Character"), + DIRECTOR(3, "Director"), + EVENT(4, "Event"), + FRANCHISE(5, "Franchise"), + GENRE(6, "Genre"), + LEAGUE(7, "League"), + POPULARITY(8, "Popularity"), + PROVIDER(9, "Provider"), + SPORT(10, "Sport"), + SPORTS_TEAM(11, "Sports Team"), + TYPE(12, "Type"), + VIDEO(13, "Video"), + SEASON(14, "Season"), + EPISODE(15, "Episode"), + ANY(16, "Any"); + + public final Integer value; + public final String label; + + private ParameterEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum MetricTypeEnum implements MatterEnum { + PIXELS(0, "Pixels"), + PERCENTAGE(1, "Percentage"); + + public final Integer value; + public final String label; + + private MetricTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class SupportedProtocolsBitmap { + public boolean dash; + public boolean hls; + + public SupportedProtocolsBitmap(boolean dash, boolean hls) { + this.dash = dash; + this.hls = hls; + } + } + + public static class FeatureMap { + /** + * + * Device supports content search (non-app specific) + */ + public boolean contentSearch; + /** + * + * Device supports basic URL-based file playback + */ + public boolean urlPlayback; + /** + * + * Enables clients to implement more advanced media seeking behavior in their user interface, such as for + * example a "seek bar". + */ + public boolean advancedSeek; + /** + * + * Device or app supports Text Tracks. + */ + public boolean textTracks; + /** + * + * Device or app supports Audio Tracks. + */ + public boolean audioTracks; + + public FeatureMap(boolean contentSearch, boolean urlPlayback, boolean advancedSeek, boolean textTracks, + boolean audioTracks) { + this.contentSearch = contentSearch; + this.urlPlayback = urlPlayback; + this.advancedSeek = advancedSeek; + this.textTracks = textTracks; + this.audioTracks = audioTracks; + } + } + + public ContentLauncherCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1290, "ContentLauncher"); + } + + protected ContentLauncherCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this shall launch the specified content with optional search criteria. This command returns a + * Launch Response. + */ + public static ClusterCommand launchContent(ContentSearchStruct search, Boolean autoPlay, String data, + PlaybackPreferencesStruct playbackPreferences, Boolean useCurrentContext) { + Map map = new LinkedHashMap<>(); + if (search != null) { + map.put("search", search); + } + if (autoPlay != null) { + map.put("autoPlay", autoPlay); + } + if (data != null) { + map.put("data", data); + } + if (playbackPreferences != null) { + map.put("playbackPreferences", playbackPreferences); + } + if (useCurrentContext != null) { + map.put("useCurrentContext", useCurrentContext); + } + return new ClusterCommand("launchContent", map); + } + + /** + * Upon receipt, this shall launch content from the specified URL. + * The content types supported include those identified in the AcceptHeader and SupportedStreamingProtocols + * attributes. + * A check shall be made to ensure the URL is secure (uses HTTPS). + * When playing a video stream in response to this command, an indication (ex. visual) of the identity of the origin + * node of the video stream shall be provided. This could be in the form of a friendly name label which uniquely + * identifies the node to the user. This friendly name label is typically assigned by the Matter Admin (ex. TV) at + * the time of commissioning and, when it’s a device, is often editable by the user. It might be a combination of a + * company name and friendly name, for example, ”Acme” or “Acme Streaming Service on Alice’s Phone”. + * This command returns a Launch Response. + */ + public static ClusterCommand launchUrl(String contentUrl, String displayString, + BrandingInformationStruct brandingInformation, PlaybackPreferencesStruct playbackPreferences) { + Map map = new LinkedHashMap<>(); + if (contentUrl != null) { + map.put("contentUrl", contentUrl); + } + if (displayString != null) { + map.put("displayString", displayString); + } + if (brandingInformation != null) { + map.put("brandingInformation", brandingInformation); + } + if (playbackPreferences != null) { + map.put("playbackPreferences", playbackPreferences); + } + return new ClusterCommand("launchUrl", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "acceptHeader : " + acceptHeader + "\n"; + str += "supportedStreamingProtocols : " + supportedStreamingProtocols + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DescriptorCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DescriptorCluster.java new file mode 100644 index 00000000000..d5bc5f79a03 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DescriptorCluster.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Descriptor + * + * @author Dan Cunningham - Initial contribution + */ +public class DescriptorCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x001D; + public static final String CLUSTER_NAME = "Descriptor"; + public static final String CLUSTER_PREFIX = "descriptor"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_DEVICE_TYPE_LIST = "deviceTypeList"; + public static final String ATTRIBUTE_SERVER_LIST = "serverList"; + public static final String ATTRIBUTE_CLIENT_LIST = "clientList"; + public static final String ATTRIBUTE_PARTS_LIST = "partsList"; + public static final String ATTRIBUTE_TAG_LIST = "tagList"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This is a list of device types and corresponding revisions declaring endpoint conformance (see DeviceTypeStruct). + * At least one device type entry shall be present. + * An endpoint shall conform to all device types listed in the DeviceTypeList. A cluster instance that is in common + * for more than one device type in the DeviceTypeList shall be supported as a shared cluster instance on the + * endpoint. + */ + public List deviceTypeList; // 0 list R V + /** + * This attribute shall list each cluster ID for the server clusters present on the endpoint instance. + */ + public List serverList; // 1 list R V + /** + * This attribute shall list each cluster ID for the client clusters present on the endpoint instance. + */ + public List clientList; // 2 list R V + /** + * This attribute indicates composition of the device type instance. Device type instance composition shall include + * the endpoints in this list. + * See Endpoint Composition for more information about which endpoints to include in this list. + */ + public List partsList; // 3 list R V + /** + * This attribute shall be used to disambiguate sibling endpoints in certain situations, as defined in the + * Disambiguation section in the System Model specification. An example of such a situation might be a device with + * two buttons, with this attribute being used to indicate which of the two endpoints corresponds to the button on + * the left side. + * It may also be used to provide information about an endpoint (e.g. the relative location of a Temperature sensor + * in a Temperature Controlled Cabinet). + * • A client SHOULD use these tags to convey disambiguation information and other relevant information to the user + * (e.g. showing it in a user interface), as appropriate. + * • A client SHOULD use these tags in its logic to make decisions, as appropriate. + * For example, a client may identify which endpoint maps to a certain function, orientation or labeling. + * A client may use the Label field of each SemanticTagStruct, if present in each structure, to indicate + * characteristics of an endpoint, or to augment what is provided in the TagID field of the same structure. + */ + public List tagList; // 4 list R V + // Structs + + /** + * The device type and revision define endpoint conformance to a release of a device type definition. See the Data + * Model specification for more information. + */ + public class DeviceTypeStruct { + /** + * This shall indicate the device type definition. The endpoint shall conform to the device type definition and + * cluster specifications required by the device type. + */ + public Integer deviceType; // devtype-id + /** + * This is the implemented revision of the device type definition. The endpoint shall conform to this revision + * of the device type. + */ + public Integer revision; // uint16 + + public DeviceTypeStruct(Integer deviceType, Integer revision) { + this.deviceType = deviceType; + this.revision = revision; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * See the Disambiguation section in the System Model spec for conformance requirements for this feature and the + * corresponding attribute. + */ + public boolean tagList; + + public FeatureMap(boolean tagList) { + this.tagList = tagList; + } + } + + public DescriptorCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 29, "Descriptor"); + } + + protected DescriptorCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "deviceTypeList : " + deviceTypeList + "\n"; + str += "serverList : " + serverList + "\n"; + str += "clientList : " + clientList + "\n"; + str += "partsList : " + partsList + "\n"; + str += "tagList : " + tagList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceEnergyManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceEnergyManagementCluster.java new file mode 100644 index 00000000000..9feb9eca0c9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceEnergyManagementCluster.java @@ -0,0 +1,1078 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * DeviceEnergyManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class DeviceEnergyManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0098; + public static final String CLUSTER_NAME = "DeviceEnergyManagement"; + public static final String CLUSTER_PREFIX = "deviceEnergyManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ESA_TYPE = "esaType"; + public static final String ATTRIBUTE_ESA_CAN_GENERATE = "esaCanGenerate"; + public static final String ATTRIBUTE_ESA_STATE = "esaState"; + public static final String ATTRIBUTE_ABS_MIN_POWER = "absMinPower"; + public static final String ATTRIBUTE_ABS_MAX_POWER = "absMaxPower"; + public static final String ATTRIBUTE_POWER_ADJUSTMENT_CAPABILITY = "powerAdjustmentCapability"; + public static final String ATTRIBUTE_FORECAST = "forecast"; + public static final String ATTRIBUTE_OPT_OUT_STATE = "optOutState"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the type of ESA. + * This attribute enables an EMS to understand some of the basic properties about how the energy may be consumed, + * generated, and stored by the ESA. + * For example, the heat energy converted by a heat pump will naturally be lost through the building to the outdoor + * environment relatively quickly, compared to storing heat in a well-insulated hot water tank. Similarly, battery + * storage and EVs can store electrical energy for much longer durations. + * This attribute can also help the EMS display information to a user and to make basic assumptions about typical + * best use of energy. For example, an EVSE may not always have an EV plugged in, so knowing the type of ESA that is + * being controlled can allow advanced energy management strategies. + */ + public ESATypeEnum esaType; // 0 ESATypeEnum R V + /** + * Indicates whether the ESA is classed as a generator or load. This allows an EMS to understand whether the power + * values reported by the ESA need to have their sign inverted when dealing with forecasts and adjustments. + * For example, a solar PV inverter (being a generator) may produce negative values to indicate generation (since + * power is flowing out of the node into the home), however a display showing the power to the consumers may need to + * present a positive solar production value to the consumer. + * For example, a home battery storage system (BESS) which needs to charge the battery and then discharge to the + * home loads, would be classed as a generator. These types of devices shall have this field set to true. When + * generating its forecast or advertising its PowerAdjustmentCapability, the power values shall be negative to + * indicate discharging to the loads in the home, and positive to indicate when it is charging its battery. + * GRID meter = Σ LoadPowers + Σ GeneratorPowers + * Example: + */ + public Boolean esaCanGenerate; // 1 bool R V + /** + * Indicates the current state of the ESA. + * If the ESA is in the Offline or Fault state it cannot be controlled by an EMS, and may not be able to report its + * Forecast information. An EMS may subscribe to the ESAState to get notified about changes in operational state. + * The ESA may have a local user interface to allow a service technician to put the ESA into Offline mode, for + * example to avoid the EMS accidentally starting or stopping the appliance when it is being serviced or tested. + */ + public ESAStateEnum esaState; // 2 ESAStateEnum R V + /** + * Indicates the minimum electrical power that the ESA can consume when switched on. This does not include when in + * power save or standby modes. + * > [!NOTE] + * > For Generator ESAs that can discharge an internal battery (such as a battery storage inverter) to loads in + * the home, the AbsMinPower will be a negative number representing the maximum power that the ESA can discharge its + * internal battery. + */ + public BigInteger absMinPower; // 3 power-mW R V + /** + * Indicates the maximum electrical power that the ESA can consume when switched on. + * Note that for Generator ESAs that can charge a battery by importing power into the node (such as a battery + * storage inverter), the AbsMaxPower will be a positive number representing the maximum power at which the ESA can + * charge its internal battery. + * For example, a battery storage inverter that can charge its battery at a maximum power of 2000W and can discharge + * the battery at a maximum power of 3000W, would have a AbsMinPower: -3000, AbsMaxPower: 2000W. + */ + public BigInteger absMaxPower; // 4 power-mW R V + /** + * Indicates how the ESA can be adjusted at the current time, and the state of any active adjustment. + * A null value indicates that no power adjustment is currently possible, and nor is any adjustment currently + * active. + * This attribute SHOULD be updated periodically by ESAs to reflect any changes in internal state, for example + * temperature or stored energy, which would affect the power or duration limits. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once every 10 seconds on changes, or + * • When it changes from null to any other value and vice versa. + */ + public PowerAdjustCapabilityStruct powerAdjustmentCapability; // 5 PowerAdjustCapabilityStruct R V + /** + * This attribute allows an ESA to share its intended forecast with a client (such as an Energy Management System). + * A null value indicates that there is no forecast currently available (for example, a program has not yet been + * selected by the user). + * A server may reset this value attribute to null on a reboot, and it does not need to persist any previous + * forecasts. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once every 10 seconds on changes, or + * • When it changes from null to any other value and vice versa, or + * • As a result of a command which causes the forecast to be updated, or + * • As a result of a change in the opt-out status which in turn may cause the ESA to recalculate its forecast. + */ + public ForecastStruct forecast; // 6 ForecastStruct R V + /** + * Indicates the current Opt-Out state of the ESA. The ESA may have a local user interface to allow the user to + * control this OptOutState. An EMS may subscribe to the OptOutState to get notified about changes in operational + * state. + * If the ESA is in the LocalOptOut or OptOut states, so it cannot be controlled by an EMS for local optimization + * reasons, it shall reject any commands which have the AdjustmentCauseEnum value LocalOptimization. If the ESA is + * in the GridOptOut or OptOut states, so it cannot be controlled by an EMS for grid optimization reasons, it shall + * reject any commands which have the AdjustmentCauseEnum value GridOptimization. + * If the user changes the Opt-Out state of the ESA which is currently operating with a Forecast that is due to a + * previous StartTimeAdjustRequest, ModifyForecastRequest or RequestConstraintBasedForecast command that would now + * not be permitted due to the new Opt-out state (i.e. the Forecast attribute ForecastUpdateReason field currently + * contains a reason which is now opted out), the ESA shall behave as if it had received a CancelRequest command. + * If the user changes the Opt-Out state of the ESA which currently has the ESAStateEnum with value Paused due to a + * previous PauseRequest command that would now not be permitted due to the new Opt-out state, and the ESA supports + * the PFR or SFR features (i.e. the Forecast attribute ForecastUpdateReason field currently contains a reason which + * is now opted out), the ESA shall behave as if it had received a ResumeRequest command. + * If the user changes the Opt-Out state of the ESA which currently has the ESAStateEnum with value + * PowerAdjustActive due to a previous PowerAdjustRequest command that would now not be permitted due to the new + * Opt-out state (i.e. the Forecast attribute ForecastUpdateReason field currently contains a reason which is now + * opted out), the ESA shall behave as if it had received a CancelPowerAdjustRequest command. + * If the ESA is in the LocalOptOut, GridOptOut, or NoOptOut states, the device is still permitted to optimize its + * own energy usage, for example, using tariff information it may obtain. + */ + public OptOutStateEnum optOutState; // 7 OptOutStateEnum R V + // Structs + + /** + * This event shall be generated when the Power Adjustment session is started. + */ + public class PowerAdjustStart { + public PowerAdjustStart() { + } + } + + /** + * This event shall be generated when the Power Adjustment session ends. + */ + public class PowerAdjustEnd { + /** + * This field shall indicate the reason why the power adjustment session ended. + */ + public CauseEnum cause; // CauseEnum + /** + * This field shall indicate the number of seconds that the power adjustment session lasted before ending. + */ + public Integer duration; // elapsed-s + /** + * This field shall indicate the approximate energy used by the ESA during the session. + * For example, if the ESA was on and was adjusted to be switched off, then this shall be 0 mWh. If this was a + * battery inverter that was requested to discharge it would have a negative EnergyUse value. If this was a + * normal load that was turned on, then it will have positive value. + */ + public BigInteger energyUse; // energy-mWh + + public PowerAdjustEnd(CauseEnum cause, Integer duration, BigInteger energyUse) { + this.cause = cause; + this.duration = duration; + this.energyUse = energyUse; + } + } + + /** + * This event shall be generated when the ESA enters the Paused state. There is no data for this event. + */ + public class Paused { + public Paused() { + } + } + + /** + * This event shall be generated when the ESA leaves the Paused state and resumes operation. + */ + public class Resumed { + /** + * This field shall indicate the reason why the pause ended. + */ + public CauseEnum cause; // CauseEnum + + public Resumed(CauseEnum cause) { + this.cause = cause; + } + } + + /** + * This indicates a generic mechanism for expressing cost to run an appliance, in terms of financial, GHG emissions, + * comfort value etc. + */ + public class CostStruct { + /** + * This field shall indicate the type of cost being represented (see CostTypeEnum). + */ + public CostTypeEnum costType; // CostTypeEnum + /** + * This field shall indicate the value of the cost. This may be negative (indicating that it is not a cost, but + * a free benefit). + * For example, if the Value was -302 and DecimalPoints was 2, then this would represent a benefit of 3.02. + */ + public Integer value; // int32 + /** + * This field shall indicate the number of digits to the right of the decimal point in the Value field. For + * example, if the Value was 102 and DecimalPoints was 2, then this would represent a cost of 1.02. + */ + public Integer decimalPoints; // uint8 + /** + * Indicates the currency for the value in the Value field. The value of the currency field shall match the + * values defined by [ISO 4217]. + * This is an optional field. It shall be included if CostType is Financial. + */ + public Integer currency; // uint16 + + public CostStruct(CostTypeEnum costType, Integer value, Integer decimalPoints, Integer currency) { + this.costType = costType; + this.value = value; + this.decimalPoints = decimalPoints; + this.currency = currency; + } + } + + public class PowerAdjustStruct { + /** + * This field shall indicate the minimum power that the ESA can have its power adjusted to. + * Note that this is a signed value. Negative values indicate power flows out of the node + * discharging a battery). + */ + public BigInteger minPower; // power-mW + /** + * This field shall indicate the maximum power that the ESA can have its power adjusted to. + * Note that this is a signed value. Negative values indicate power flows out of the node (e.g. discharging a + * battery). + * For example, if the charging current of an EVSE can be adjusted within the range of 6A to 32A on a 230V + * supply, then the power adjustment range is between 1380W and 7360W. Here the MinPower would be 1380W, and + * MaxPower would be 7360W. + * For example, if a battery storage inverter can discharge between 0 to 3000W towards a load, then power is + * flowing out of the node and is therefore negative. Its MinPower would be -3000W and its MaxPower would be 0W. + * In another example, if a battery storage inverter can charge its internal battery, between 0W and 2000W. Here + * power is flowing into the node when charging. As such the MinPower becomes 0W and MaxPower becomes 2000W. + */ + public BigInteger maxPower; // power-mW + /** + * This field shall indicate the minimum duration, in seconds, that a controller may invoke an ESA power + * adjustment. Manufacturers may use this to as an anti-cycling capability to avoid controllers from rapidly + * making power adjustments. + */ + public Integer minDuration; // elapsed-s + /** + * This field shall indicate the maximum duration, in seconds, that a controller may invoke an ESA power + * adjustment. Manufacturers may use this to protect the user experience, to avoid over heating of the ESA, + * ensuring that there is sufficient headroom to use or store energy in the ESA or for any other reason. + */ + public Integer maxDuration; // elapsed-s + + public PowerAdjustStruct(BigInteger minPower, BigInteger maxPower, Integer minDuration, Integer maxDuration) { + this.minPower = minPower; + this.maxPower = maxPower; + this.minDuration = minDuration; + this.maxDuration = maxDuration; + } + } + + public class PowerAdjustCapabilityStruct { + /** + * This field shall indicate how the ESA can be adjusted at the current time. + * For example, a battery storage inverter may need to regulate its internal temperature, or the charging rate + * of the battery may be limited due to cold temperatures, or a change in the state of charge of the battery may + * mean that the maximum charging or discharging rate is limited. + * An empty list shall indicate that no power adjustment is currently possible. + * Multiple entries in the list allow indicating that permutations of scenarios may be possible. + * For example, a 10kWh battery could be at 80% state of charge. If charging at 2kW, then it would be full in 1 + * hour. However, it could be discharged at 2kW for 4 hours. + * In this example the list of PowerAdjustStructs allows multiple scenarios to be offered as follows: + */ + public List powerAdjustCapability; // list + public PowerAdjustReasonEnum cause; // PowerAdjustReasonEnum + + public PowerAdjustCapabilityStruct(List powerAdjustCapability, PowerAdjustReasonEnum cause) { + this.powerAdjustCapability = powerAdjustCapability; + this.cause = cause; + } + } + + /** + * This indicates a list of 'slots' describing the overall timing of the ESA’s planned energy and power + * use, with different power and energy demands per slot. For example, slots might be used to describe the distinct + * stages of a washing machine cycle. + * Where an ESA does not know the actual power and energy use of the system, it may support the SFR feature and + * instead report its internal state. + */ + public class ForecastStruct { + /** + * This field shall indicate the sequence number for the current forecast. If the ESA updates a forecast, it + * shall monotonically increase this value. + * The ESA does not need to persist this value across reboots, since the EMS SHOULD be able to detect that any + * previous subscriptions are lost if a device reboots. The loss of a subscription and subsequent + * re-subscription allows the EMS to learn about any new forecasts. + * The value of ForecastID is allowed to wrap. + */ + public Integer forecastId; // uint32 + /** + * This field shall indicate which element of the Slots list is currently active in the Forecast sequence. A + * null value indicates that the sequence has not yet started. + */ + public Integer activeSlotNumber; // uint16 + /** + * This field shall indicate the planned start time, in UTC, for the entire Forecast. + */ + public Integer startTime; // epoch-s + /** + * This field shall indicate the planned end time, in UTC, for the entire Forecast. + */ + public Integer endTime; // epoch-s + /** + * This field shall indicate the earliest start time, in UTC, that the entire Forecast can be shifted to. A null + * value indicates that it can be started immediately. + */ + public Integer earliestStartTime; // epoch-s + /** + * This field shall indicate the latest end time, in UTC, for the entire Forecast. + * e.g. for an EVSE charging session, this may indicate the departure time for the vehicle, by which time the + * charging session must end. + */ + public Integer latestEndTime; // epoch-s + /** + * This field shall indicate that some part of the Forecast can be paused. It aims to allow a client to read + * this flag and if it is false, then none of the slots contain SlotIsPausable set to true. This can save a + * client from having to check each slot in the list. + */ + public Boolean isPausable; // bool + /** + * This field shall contain a list of SlotStructs. + * It shall contain at least 1 entry, and a maximum of 10. + */ + public List slots; // list + /** + * This field shall contain the reason the current Forecast was generated. + */ + public ForecastUpdateReasonEnum forecastUpdateReason; // ForecastUpdateReasonEnum + + public ForecastStruct(Integer forecastId, Integer activeSlotNumber, Integer startTime, Integer endTime, + Integer earliestStartTime, Integer latestEndTime, Boolean isPausable, List slots, + ForecastUpdateReasonEnum forecastUpdateReason) { + this.forecastId = forecastId; + this.activeSlotNumber = activeSlotNumber; + this.startTime = startTime; + this.endTime = endTime; + this.earliestStartTime = earliestStartTime; + this.latestEndTime = latestEndTime; + this.isPausable = isPausable; + this.slots = slots; + this.forecastUpdateReason = forecastUpdateReason; + } + } + + /** + * This indicates a specific stage of an ESA’s operation. + */ + public class SlotStruct { + /** + * This field shall indicate the minimum time (in seconds) that the appliance expects to be in this slot for. + */ + public Integer minDuration; // elapsed-s + /** + * This field shall indicate the maximum time (in seconds) that the appliance expects to be in this slot for. + */ + public Integer maxDuration; // elapsed-s + /** + * This field shall indicate the expected time (in seconds) that the appliance expects to be in this slot for. + */ + public Integer defaultDuration; // elapsed-s + /** + * This field shall indicate the time (in seconds) that has already elapsed whilst in this slot. If the slot has + * not yet been started, then it shall be 0. Once the slot has been completed, then this reflects how much time + * was spent in this slot. + * When subscribed to, a change in this field value shall NOT cause the Forecast attribute to be updated since + * this value may change every 1 second. + * When the Forecast attribute is read, then this value shall be the most recent value. + */ + public Integer elapsedSlotTime; // elapsed-s + /** + * This field shall indicate the time (in seconds) that is estimated to be remaining. + * Note that it may not align to the DefaultDuration - ElapsedSlotTime since an appliance may have revised its + * planned operation based on conditions. + * When subscribed to, a change in this field value shall NOT cause the Forecast attribute to be updated, since + * this value may change every 1 second. + * Note that if the ESA is currently paused, then this value shall NOT change. + * When the Forecast attribute is read, then this value shall be the most recent value. + */ + public Integer remainingSlotTime; // elapsed-s + /** + * This field shall indicate whether this slot can be paused. + */ + public Boolean slotIsPausable; // bool + /** + * This field shall indicate the shortest period that the slot can be paused for. This can be set to avoid + * controllers trying to pause ESAs for short periods and then resuming operation in a cyclic fashion which may + * damage or cause excess energy to be consumed with restarting of an operation. + */ + public Integer minPauseDuration; // elapsed-s + /** + * This field shall indicate the longest period that the slot can be paused for. + */ + public Integer maxPauseDuration; // elapsed-s + /** + * This field shall indicate a manufacturer defined value indicating the state of the ESA. + * This may be used by an observing EMS which also has access to the metering data to ascertain the typical + * power drawn when the ESA is in a manufacturer defined state. + * Some appliances, such as smart thermostats, may not know how much power is being drawn by the HVAC system, + * but do know what they have asked the HVAC system to do. + * Manufacturers can use this value to indicate a variety of states in an unspecified way. For example, they may + * choose to use values between 0-100 as a percentage of compressor modulation, or could use these values as + * Enum states meaning heating with fan, heating without fan etc. + * By providing this information a smart EMS may be able to learn the observed power draw when the ESA is put + * into a specific state. It can potentially then use the ManufacturerESAState field in the Forecast attribute + * along with observed power drawn to predict the power draw from the appliance and potentially ask it to modify + * its timing via one of the adjustment request commands, or adjust other ESAs power to compensate. + */ + public Integer manufacturerEsaState; // uint16 + /** + * This field shall indicate the expected power that the appliance will use during this slot. It may be + * considered the average value over the slot, and some variation from this would be expected (for example, as + * it is ramping up). + */ + public BigInteger nominalPower; // power-mW + /** + * This field shall indicate the lowest power that the appliance expects to use during this slot. (e.g. during a + * ramp up it may be 0W). + * Some appliances (e.g. battery inverters which can charge and discharge) may have a negative power. + */ + public BigInteger minPower; // power-mW + /** + * This field shall indicate the maximum power that the appliance expects to use during this slot. (e.g. during + * a ramp up it may be 0W). This field ignores the effects of short-lived inrush currents. + * Some appliances (e.g. battery inverters which can charge and discharge) may have a negative power. + */ + public BigInteger maxPower; // power-mW + /** + * This field shall indicate the expected energy that the appliance expects to use or produce during this slot. + * Some appliances (e.g. battery inverters which can charge and discharge) may have a negative energy. + */ + public BigInteger nominalEnergy; // energy-mWh + /** + * This field shall indicate the current estimated cost for operating. + * For example, if the device has access to an Energy pricing server it may be able to use the tariff to + * estimate the cost of energy for this slot in the power forecast. + * When an Energy Management System requests a change in the schedule, then the device may suggest a change in + * the cost as a result of shifting its energy. This can allow a demand side response service to be informed of + * the relative cost to use energy at a different time. + * The Costs field is a list of CostStruct structures which allows multiple CostTypeEnum and Values to be shared + * by the energy appliance. These could be based on GHG emissions, comfort value for the consumer etc. + * For example, comfort could be expressed in abstract units or in currency. A water heater that is heated + * earlier in the day is likely to lose some of its heat before it is needed, which could require a top-up + * heating event to occur later in the day (which may incur additional cost). + * If the ESA cannot calculate its cost for any reason (such as losing its connection to a Price server) it may + * omit this field. This is treated as extra meta data that an EMS may use to optimize a system. + */ + public List costs; // list + /** + * This field shall indicate the minimum power that the appliance can be requested to use. + * For example, some EVSEs cannot be switched on to charge below 6A which may equate to ~1.3kW in EU markets. If + * the slot indicates a NominalPower of 0W (indicating it is expecting to be off), this allows an ESA to + * indicate it could be switched on to charge, but this would be the minimum power limit it can be set to. + */ + public BigInteger minPowerAdjustment; // power-mW + /** + * This field shall indicate the maximum power that the appliance can be requested to use. + * For example, an EVSE may be limited by its electrical supply to 32A which would be ~7.6kW in EU markets. If + * the slot indicates a NominalPower of 0W (indicating it is expecting to be off), this allows an ESA to + * indicate it could be switched on to charge, but this would be the maximum power limit it can be set to. + */ + public BigInteger maxPowerAdjustment; // power-mW + /** + * This field shall indicate the minimum time, in seconds, that the slot can be requested to shortened to. + * For example, if the slot indicates a NominalPower of 0W (indicating it is expecting to be off), this would + * allow an ESA to specify the minimum time it could be switched on for. This is to help protect the appliance + * from being damaged by short cycling times. + * For example, a heat pump compressor may have a minimum cycle time of order a few minutes. + */ + public Integer minDurationAdjustment; // elapsed-s + /** + * This field shall indicate the maximum time, in seconds, that the slot can be requested to extended to. + * For example, if the slot indicates a NominalPower of 0W (indicating it is expecting to be off), this allows + * an ESA to specify the maximum time it could be switched on for. This may allow a battery or water heater to + * indicate the maximum duration that it can charge for before becoming full. In the case of a battery inverter + * which can be discharged, it may equally indicate the maximum time the battery could be discharged for (at the + * MaxPowerAdjustment power level). + */ + public Integer maxDurationAdjustment; // elapsed-s + + public SlotStruct(Integer minDuration, Integer maxDuration, Integer defaultDuration, Integer elapsedSlotTime, + Integer remainingSlotTime, Boolean slotIsPausable, Integer minPauseDuration, Integer maxPauseDuration, + Integer manufacturerEsaState, BigInteger nominalPower, BigInteger minPower, BigInteger maxPower, + BigInteger nominalEnergy, List costs, BigInteger minPowerAdjustment, + BigInteger maxPowerAdjustment, Integer minDurationAdjustment, Integer maxDurationAdjustment) { + this.minDuration = minDuration; + this.maxDuration = maxDuration; + this.defaultDuration = defaultDuration; + this.elapsedSlotTime = elapsedSlotTime; + this.remainingSlotTime = remainingSlotTime; + this.slotIsPausable = slotIsPausable; + this.minPauseDuration = minPauseDuration; + this.maxPauseDuration = maxPauseDuration; + this.manufacturerEsaState = manufacturerEsaState; + this.nominalPower = nominalPower; + this.minPower = minPower; + this.maxPower = maxPower; + this.nominalEnergy = nominalEnergy; + this.costs = costs; + this.minPowerAdjustment = minPowerAdjustment; + this.maxPowerAdjustment = maxPowerAdjustment; + this.minDurationAdjustment = minDurationAdjustment; + this.maxDurationAdjustment = maxDurationAdjustment; + } + } + + public class SlotAdjustmentStruct { + /** + * This field shall indicate the index into the Slots list within the Forecast that is to be modified. It shall + * be less than the actual length of the Slots list (implicitly it must be in the range 0 to 9 based on the + * maximum length of the Slots list constraint). + */ + public Integer slotIndex; // uint8 + /** + * This field shall indicate the new requested power that the ESA shall operate at. It MUST be between the + * AbsMinPower and AbsMaxPower attributes as advertised by the ESA if it supports PFR. + * This is a signed value and can be used to indicate charging or discharging. If the ESA does NOT support PFR + * this value shall be ignored by the ESA. + */ + public BigInteger nominalPower; // power-mW + /** + * This field shall indicate the new requested duration, in seconds, that the ESA shall extend or shorten the + * slot duration to. It MUST be between the MinDurationAdjustment and MaxDurationAdjustment for the slot as + * advertised by the ESA. + */ + public Integer duration; // elapsed-s + + public SlotAdjustmentStruct(Integer slotIndex, BigInteger nominalPower, Integer duration) { + this.slotIndex = slotIndex; + this.nominalPower = nominalPower; + this.duration = duration; + } + } + + /** + * The ConstraintsStruct allows a client to inform an ESA about a constraint period (such as a grid event, or + * perhaps excess solar PV). The format allows the client to suggest that the ESA can either turn up its energy + * consumption, or turn down its energy consumption during this period. + */ + public class ConstraintsStruct { + /** + * This field shall indicate the start time of the constraint period that the client wishes the ESA to compute a + * new Forecast. + * This value is in UTC and MUST be in the future. + */ + public Integer startTime; // epoch-s + /** + * This field shall indicate the duration of the constraint in seconds. + */ + public Integer duration; // elapsed-s + /** + * This field shall indicate the nominal power that client wishes the ESA to operate at during the constrained + * period. It MUST be between the AbsMinPower and AbsMaxPower attributes as advertised by the ESA if it supports + * PFR. + * This is a signed value and can be used to indicate charging or discharging. + */ + public BigInteger nominalPower; // power-mW + /** + * This field shall indicate the maximum energy that can be transferred to or from the ESA during the constraint + * period. + * This is a signed value and can be used to indicate charging or discharging. + */ + public BigInteger maximumEnergy; // energy-mWh + /** + * This field shall indicate the turn up or turn down nature that the grid wants as the outcome by the ESA + * during the constraint period. + * This is expressed as a signed value between -100 to +100. A value of 0 would indicate no bias to using more + * or less energy. A negative value indicates a request to use less energy. A positive value indicates a request + * to use more energy. + * Note that the mapping between values and operation is manufacturer specific. + */ + public Integer loadControl; // int8 + + public ConstraintsStruct(Integer startTime, Integer duration, BigInteger nominalPower, BigInteger maximumEnergy, + Integer loadControl) { + this.startTime = startTime; + this.duration = duration; + this.nominalPower = nominalPower; + this.maximumEnergy = maximumEnergy; + this.loadControl = loadControl; + } + } + + // Enums + public enum CostTypeEnum implements MatterEnum { + FINANCIAL(0, "Financial"), + GHG_EMISSIONS(1, "Ghg Emissions"), + COMFORT(2, "Comfort"), + TEMPERATURE(3, "Temperature"); + + public final Integer value; + public final String label; + + private CostTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ESATypeEnum implements MatterEnum { + EVSE(0, "Evse"), + SPACE_HEATING(1, "Space Heating"), + WATER_HEATING(2, "Water Heating"), + SPACE_COOLING(3, "Space Cooling"), + SPACE_HEATING_COOLING(4, "Space Heating Cooling"), + BATTERY_STORAGE(5, "Battery Storage"), + SOLAR_PV(6, "Solar Pv"), + FRIDGE_FREEZER(7, "Fridge Freezer"), + WASHING_MACHINE(8, "Washing Machine"), + DISHWASHER(9, "Dishwasher"), + COOKING(10, "Cooking"), + HOME_WATER_PUMP(11, "Home Water Pump"), + IRRIGATION_WATER_PUMP(12, "Irrigation Water Pump"), + POOL_PUMP(13, "Pool Pump"), + OTHER(255, "Other"); + + public final Integer value; + public final String label; + + private ESATypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ESAStateEnum implements MatterEnum { + OFFLINE(0, "Offline"), + ONLINE(1, "Online"), + FAULT(2, "Fault"), + POWER_ADJUST_ACTIVE(3, "Power Adjust Active"), + PAUSED(4, "Paused"); + + public final Integer value; + public final String label; + + private ESAStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum OptOutStateEnum implements MatterEnum { + NO_OPT_OUT(0, "No Opt Out"), + LOCAL_OPT_OUT(1, "Local Opt Out"), + GRID_OPT_OUT(2, "Grid Opt Out"), + OPT_OUT(3, "Opt Out"); + + public final Integer value; + public final String label; + + private OptOutStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum CauseEnum implements MatterEnum { + NORMAL_COMPLETION(0, "Normal Completion"), + OFFLINE(1, "Offline"), + FAULT(2, "Fault"), + USER_OPT_OUT(3, "User Opt Out"), + CANCELLED(4, "Cancelled"); + + public final Integer value; + public final String label; + + private CauseEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum AdjustmentCauseEnum implements MatterEnum { + LOCAL_OPTIMIZATION(0, "Local Optimization"), + GRID_OPTIMIZATION(1, "Grid Optimization"); + + public final Integer value; + public final String label; + + private AdjustmentCauseEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ForecastUpdateReasonEnum implements MatterEnum { + INTERNAL_OPTIMIZATION(0, "Internal Optimization"), + LOCAL_OPTIMIZATION(1, "Local Optimization"), + GRID_OPTIMIZATION(2, "Grid Optimization"); + + public final Integer value; + public final String label; + + private ForecastUpdateReasonEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum PowerAdjustReasonEnum implements MatterEnum { + NO_ADJUSTMENT(0, "No Adjustment"), + LOCAL_OPTIMIZATION_ADJUSTMENT(1, "Local Optimization Adjustment"), + GRID_OPTIMIZATION_ADJUSTMENT(2, "Grid Optimization Adjustment"); + + public final Integer value; + public final String label; + + private PowerAdjustReasonEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * For Energy Smart Appliances (ESA) the definition of being 'smart' mandates that they can report + * their current power adjustment capability and have an EMS request a temporary adjustment. This may typically + * be to curtail power requirements during peak periods, but can also be used to turn on an ESA if there is + * excess renewable or local generation (Solar PV). + * For example, a home may have solar PV which often produces more power than the home requires, resulting in + * the excess power flowing into the grid. This excess power naturally fluctuates when clouds pass overhead and + * other loads in the home are switched on and off. + */ + public boolean powerAdjustment; + /** + * + * For Energy Smart Appliances (ESA) the definition of being 'smart' implies that they can report + * their indicative forecast power demands or generation, to a greater or lesser extent. For some ESAs this is + * highly predictable (in terms of both power and time), in other appliances this is more challenging and only a + * basic level of forecast is possible. + * Forecasts are defined from a current time, using a slot format, where the slot is akin to a relatively + * constant operating mode. + * In some circumstances the ESA may allow the stage to be delayed or paused (subject to safety and + * manufacturer’s discretion and user preferences). + * Typically, appliances with a heating element cannot have their power consumption adjusted and can only be + * paused or delayed. + * Some ESAs may not be flexible other than a delayed cycle start (for example, once the washing cycle has been + * started then they run continuously until the cycle completes). + * Appliances that only support the PowerForecastReporting and not any of the adjustment features may indicate + * that they are not flexible in the forecast slot format. + * The PowerForecastReporting and the adjustment features aim to align to the [SAREF4ENER] ontology. + * For example, a single phase EVSE can be adjusted in the range of 6-32Amps in 0.6 Amp steps in EU or on a + * hardwired 120V supply in the range of 6-15 Amps in US. + * For example, a home battery may be adjusted to charge or discharge in steps of 1W. + * For example, a heat pump may be able to modulate its compressor inverter between 20-100% of its rated power. + * The ESA indicates its power adjustment range and its nominal power consumption as part of its Forecast. + */ + public boolean powerForecastReporting; + /** + * + * Some ESAs do not know their actual power consumption, but do know the state of operation. Like the + * PowerForecastingReporting feature, this uses the same slot structure mechanism to indicate a change in state + * vs time. + * An external observing EMS may have access to real-time meter readings, and could learn the typical power + * consumption based on the advertised internal state of the ESA. + * To enable this capability, the ESA shall report its internal operational state using an manufacturer specific + * value. + * Once the EMS has built a model of the state vs observed power consumption, it may request a forecast + * adjustment for particular times of the day, encouraging the ESA to use power at alternative times. + */ + public boolean stateForecastReporting; + /** + * + * ESAs which support the Start Time Adjustment feature, allow an EMS to recommend a change to the start time of + * the energy transfer that the ESA has previously suggested it would use. + * However, the EMS is aware that a grid event has occurred, making it cheaper to run the cycle at a later time, + * but the washing machine is not aware of this. + * The EMS first requests the Forecast data from each of its registered ESAs. It determines that the washing + * machine has a power profile suggesting it will start the wash cycle at 9pm, but the EMS now knows that the + * grid event means it will be cheaper to delay the start until 11pm. + * The EMS can then optimize the cost by asking the washing machine to delay starting the wash cycle until 11pm. + * It does this by sending a StartTimeAdjustRequest to the washing machine to request delaying the start of the + * washing cycle. + */ + public boolean startTimeAdjustment; + /** + * + * ESAs which support the Pausable feature, allow an EMS to recommend a pause in the middle of a forecast power + * profile that the ESA is currently using. + * However, the EMS becomes aware from the smart meter that the total home load on the grid is close to + * exceeding its allowed total grid load. + * The EMS first requests the Forecast data from each of its registered ESAs. It determines that the washing + * machine has a power profile suggesting its current step in the wash cycle is using power to heat the water, + * but that this step can be paused. + * The EMS can then reduce the grid load by asking the washing machine to pause the wash cycle for a short + * duration. + * It does this by sending a PauseRequest to the washing machine to request pausing the current step of the + * forecast power usage for a period to allow other home loads to finish before resuming the washing cycle. + */ + public boolean pausable; + /** + * + * ESAs which support the Forecast adjustment feature, allow an EMS to recommend a change to the start, duration + * and/or power level limits of the steps of the power profile that the ESA has previously suggested it would + * use. + * However, the hot water tank is likely to need to be reheated before the homeowner comes home in the evening. + * The heat pump is not aware that the property also has a solar PV inverter which is also an ESA that is + * communicating with the EMS. + * The EMS first requests the Forecast data from each of its registered ESAs. It determines that the heat pump + * has a power profile suggesting it needs to heat hot water around 6pm. The solar PV inverter has forecast that + * it will generate 3.6kW of power during the middle of the day and into the afternoon before the sun goes down. + * The EMS can then optimize the home considering other non-ESA loads and can ask the heat pump to heat the hot + * water around 3pm when it has forecast that excess solar power will be available. + * It does this by sending a ModifyForecastRequest to the heat pump and asks the heat pump to expect to run at a + * lower power consumption (within the solar excess power) which requires the heat pump to run for a longer + * duration to achieve its required energy demand. + */ + public boolean forecastAdjustment; + /** + * + * ESAs which support the Constraint-Based Adjustment feature allow an EMS to inform the ESA of periods during + * which power usage should be modified (for example when the EMS has been made aware that the grid supplier has + * requested reduced energy usage due to overall peak grid demand) and may cause the ESA to modify the intended + * power profile has previously suggested it would use. + * However, the DSR service provider has informed the EMS that due to high forecast winds it is now forecast + * that there will be very cheap energy available from wind generation between 2am and 3am. + * The EMS first requests the Forecast data from each of its registered ESAs. It determines that the EVSE has a + * power profile suggesting it plans to start charging the vehicle at 1am. + * The EMS can then try to reduce the cost of charging the EV by informing the EVSE of the desire to increase + * the charging between scheduled times. + * It does this by sending a RequestConstraintBasedForecast to the EVSE and asks it to run at a higher + * NominalPower consumption during the constraint period, which may require it to decrease its charge rate + * outside the constraint period to achieve its required energy demand. + */ + public boolean constraintBasedAdjustment; + + public FeatureMap(boolean powerAdjustment, boolean powerForecastReporting, boolean stateForecastReporting, + boolean startTimeAdjustment, boolean pausable, boolean forecastAdjustment, + boolean constraintBasedAdjustment) { + this.powerAdjustment = powerAdjustment; + this.powerForecastReporting = powerForecastReporting; + this.stateForecastReporting = stateForecastReporting; + this.startTimeAdjustment = startTimeAdjustment; + this.pausable = pausable; + this.forecastAdjustment = forecastAdjustment; + this.constraintBasedAdjustment = constraintBasedAdjustment; + } + } + + public DeviceEnergyManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 152, "DeviceEnergyManagement"); + } + + protected DeviceEnergyManagementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Allows a client to request an adjustment in the power consumption of an ESA for a specified duration. + */ + public static ClusterCommand powerAdjustRequest(BigInteger power, Integer duration, AdjustmentCauseEnum cause) { + Map map = new LinkedHashMap<>(); + if (power != null) { + map.put("power", power); + } + if (duration != null) { + map.put("duration", duration); + } + if (cause != null) { + map.put("cause", cause); + } + return new ClusterCommand("powerAdjustRequest", map); + } + + /** + * Allows a client to cancel an ongoing PowerAdjustmentRequest operation. + */ + public static ClusterCommand cancelPowerAdjustRequest() { + return new ClusterCommand("cancelPowerAdjustRequest"); + } + + /** + * Allows a client to adjust the start time of a Forecast sequence that has not yet started operation (i.e. where + * the current Forecast StartTime is in the future). + */ + public static ClusterCommand startTimeAdjustRequest(Integer requestedStartTime, AdjustmentCauseEnum cause) { + Map map = new LinkedHashMap<>(); + if (requestedStartTime != null) { + map.put("requestedStartTime", requestedStartTime); + } + if (cause != null) { + map.put("cause", cause); + } + return new ClusterCommand("startTimeAdjustRequest", map); + } + + /** + * Allows a client to temporarily pause an operation and reduce the ESAs energy demand. + */ + public static ClusterCommand pauseRequest(Integer duration, AdjustmentCauseEnum cause) { + Map map = new LinkedHashMap<>(); + if (duration != null) { + map.put("duration", duration); + } + if (cause != null) { + map.put("cause", cause); + } + return new ClusterCommand("pauseRequest", map); + } + + /** + * Allows a client to cancel the PauseRequest command and enable earlier resumption of operation. + */ + public static ClusterCommand resumeRequest() { + return new ClusterCommand("resumeRequest"); + } + + /** + * Allows a client to modify a Forecast within the limits allowed by the ESA. + */ + public static ClusterCommand modifyForecastRequest(Integer forecastId, List slotAdjustments, + AdjustmentCauseEnum cause) { + Map map = new LinkedHashMap<>(); + if (forecastId != null) { + map.put("forecastId", forecastId); + } + if (slotAdjustments != null) { + map.put("slotAdjustments", slotAdjustments); + } + if (cause != null) { + map.put("cause", cause); + } + return new ClusterCommand("modifyForecastRequest", map); + } + + /** + * Allows a client to ask the ESA to recompute its Forecast based on power and time constraints. + */ + public static ClusterCommand requestConstraintBasedForecast(List constraints, + AdjustmentCauseEnum cause) { + Map map = new LinkedHashMap<>(); + if (constraints != null) { + map.put("constraints", constraints); + } + if (cause != null) { + map.put("cause", cause); + } + return new ClusterCommand("requestConstraintBasedForecast", map); + } + + /** + * Allows a client to request cancellation of a previous adjustment request in a StartTimeAdjustRequest, + * ModifyForecastRequest or RequestConstraintBasedForecast command. + */ + public static ClusterCommand cancelRequest() { + return new ClusterCommand("cancelRequest"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "esaType : " + esaType + "\n"; + str += "esaCanGenerate : " + esaCanGenerate + "\n"; + str += "esaState : " + esaState + "\n"; + str += "absMinPower : " + absMinPower + "\n"; + str += "absMaxPower : " + absMaxPower + "\n"; + str += "powerAdjustmentCapability : " + powerAdjustmentCapability + "\n"; + str += "forecast : " + forecast + "\n"; + str += "optOutState : " + optOutState + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceEnergyManagementModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceEnergyManagementModeCluster.java new file mode 100644 index 00000000000..0b484b03172 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceEnergyManagementModeCluster.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * DeviceEnergyManagementMode + * + * @author Dan Cunningham - Initial contribution + */ +public class DeviceEnergyManagementModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x009F; + public static final String CLUSTER_NAME = "DeviceEnergyManagementMode"; + public static final String CLUSTER_PREFIX = "deviceEnergyManagementMode"; + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + NO_OPTIMIZATION(16384, "No Optimization"), + DEVICE_OPTIMIZATION(16385, "Device Optimization"), + LOCAL_OPTIMIZATION(16386, "Local Optimization"), + GRID_OPTIMIZATION(16387, "Grid Optimization"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public DeviceEnergyManagementModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 159, "DeviceEnergyManagementMode"); + } + + protected DeviceEnergyManagementModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceTypes.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceTypes.java new file mode 100644 index 00000000000..69105587503 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DeviceTypes.java @@ -0,0 +1,528 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.util.HashMap; +import java.util.Map; + +/** + * DeviceTypes + * + * @author Dan Cunningham - Initial contribution + */ + +public class DeviceTypes { + + public static final Map DEVICE_MAPPING = new HashMap<>(); + static { + DEVICE_MAPPING.put(14, "Aggregator"); + DEVICE_MAPPING.put(45, "AirPurifier"); + DEVICE_MAPPING.put(44, "AirQualitySensor"); + DEVICE_MAPPING.put(40, "BasicVideoPlayer"); + DEVICE_MAPPING.put(24, "BatteryStorage"); + DEVICE_MAPPING.put(19, "BridgedNode"); + DEVICE_MAPPING.put(41, "CastingVideoClient"); + DEVICE_MAPPING.put(35, "CastingVideoPlayer"); + DEVICE_MAPPING.put(261, "ColorDimmerSwitch"); + DEVICE_MAPPING.put(268, "ColorTemperatureLight"); + DEVICE_MAPPING.put(21, "ContactSensor"); + DEVICE_MAPPING.put(36, "ContentApp"); + DEVICE_MAPPING.put(2112, "ControlBridge"); + DEVICE_MAPPING.put(119, "CookSurface"); + DEVICE_MAPPING.put(120, "Cooktop"); + DEVICE_MAPPING.put(1293, "DeviceEnergyManagement"); + DEVICE_MAPPING.put(257, "DimmableLight"); + DEVICE_MAPPING.put(267, "DimmablePlugInUnit"); + DEVICE_MAPPING.put(260, "DimmerSwitch"); + DEVICE_MAPPING.put(117, "Dishwasher"); + DEVICE_MAPPING.put(11, "DoorLockController"); + DEVICE_MAPPING.put(10, "DoorLock"); + DEVICE_MAPPING.put(1296, "ElectricalSensor"); + DEVICE_MAPPING.put(1292, "EnergyEvse"); + DEVICE_MAPPING.put(269, "ExtendedColorLight"); + DEVICE_MAPPING.put(122, "ExtractorHood"); + DEVICE_MAPPING.put(43, "Fan"); + DEVICE_MAPPING.put(774, "FlowSensor"); + DEVICE_MAPPING.put(15, "GenericSwitch"); + DEVICE_MAPPING.put(777, "HeatPump"); + DEVICE_MAPPING.put(775, "HumiditySensor"); + DEVICE_MAPPING.put(304, "JointFabricAdministrator"); + DEVICE_MAPPING.put(124, "LaundryDryer"); + DEVICE_MAPPING.put(115, "LaundryWasher"); + DEVICE_MAPPING.put(262, "LightSensor"); + DEVICE_MAPPING.put(121, "MicrowaveOven"); + DEVICE_MAPPING.put(39, "ModeSelect"); + DEVICE_MAPPING.put(272, "MountedDimmableLoadControl"); + DEVICE_MAPPING.put(271, "MountedOnOffControl"); + DEVICE_MAPPING.put(144, "NetworkInfrastructureManager"); + DEVICE_MAPPING.put(263, "OccupancySensor"); + DEVICE_MAPPING.put(256, "OnOffLight"); + DEVICE_MAPPING.put(259, "OnOffLightSwitch"); + DEVICE_MAPPING.put(266, "OnOffPlugInUnit"); + DEVICE_MAPPING.put(2128, "OnOffSensor"); + DEVICE_MAPPING.put(20, "OtaProvider"); + DEVICE_MAPPING.put(18, "OtaRequestor"); + DEVICE_MAPPING.put(123, "Oven"); + DEVICE_MAPPING.put(17, "PowerSource"); + DEVICE_MAPPING.put(773, "PressureSensor"); + DEVICE_MAPPING.put(772, "PumpController"); + DEVICE_MAPPING.put(771, "Pump"); + DEVICE_MAPPING.put(68, "RainSensor"); + DEVICE_MAPPING.put(112, "Refrigerator"); + DEVICE_MAPPING.put(116, "RoboticVacuumCleaner"); + DEVICE_MAPPING.put(114, "RoomAirConditioner"); + DEVICE_MAPPING.put(22, "RootNode"); + DEVICE_MAPPING.put(25, "SecondaryNetworkInterface"); + DEVICE_MAPPING.put(118, "SmokeCoAlarm"); + DEVICE_MAPPING.put(23, "SolarPower"); + DEVICE_MAPPING.put(34, "Speaker"); + DEVICE_MAPPING.put(113, "TemperatureControlledCabinet"); + DEVICE_MAPPING.put(770, "TemperatureSensor"); + DEVICE_MAPPING.put(769, "Thermostat"); + DEVICE_MAPPING.put(42, "VideoRemoteControl"); + DEVICE_MAPPING.put(65, "WaterFreezeDetector"); + DEVICE_MAPPING.put(1295, "WaterHeater"); + DEVICE_MAPPING.put(67, "WaterLeakDetector"); + DEVICE_MAPPING.put(66, "WaterValve"); + DEVICE_MAPPING.put(515, "WindowCoveringController"); + DEVICE_MAPPING.put(514, "WindowCovering"); + } + /** + * This device type aggregates endpoints as a collection. Clusters on the endpoint indicating this device type + * provide functionality for the collection of descendant endpoints present in the PartsList of the endpoint’s + * descriptor, for example the Actions cluster. + * The purpose of this device type is to aggregate functionality for a collection of endpoints. The definition of + * the collection or functionality is not defined here. + * When using this device type as a collection of bridged nodes, please see the "Bridge" section in the + * System Model specification. + **/ + public static final Integer AGGREGATOR = 14; + /** + * An Air Purifier is a standalone device that is designed to clean the air in a room. + * It is a device that has a fan to control the air speed while it is operating. Optionally, it can report on the + * condition of its filters. + **/ + public static final Integer AIR_PURIFIER = 45; + /** + * This defines conformance for the Air Quality Sensor device type. + * An air quality sensor is a device designed to monitor and measure various parameters related to the quality of + * ambient air in indoor or outdoor environments. + **/ + public static final Integer AIR_QUALITY_SENSOR = 44; + /** + * This defines conformance to the Basic Video Player device type. + * A Video Player (either Basic or Casting) represents a device that is able to play media to a physical output or + * to a display screen which is part of the device. + * A Basic Video Player has playback controls (play, pause, etc.) and keypad remote controls (up, down, number + * input), but is not able to launch content and is not a content app platform (the Casting Video Player device type + * is used for these functions). + * For example, a Basic Video Player can be a traditional TV device a physical media playback device such as a DVD + * Player, or a device that provides input to another device like a TV or computer monitor. + * Please see Video Player Architecture for additional Basic Video Player requirements relating to Video Player + * device endpoint composition, commissioning, feature representation in clusters, and UI context. + **/ + public static final Integer BASIC_VIDEO_PLAYER = 40; + /** + * A Battery Storage device is a device that allows a DC battery, which can optionally be comprised of a set + * parallel strings of battery packs and associated controller, and an AC inverter, to be monitored and controlled + * by an Energy Management System in order to manage the peaks and troughs of supply and demand, and/or to optimize + * cost of the energy consumed in premises. It is not intended to be used for a UPS directly supplying a set of + * appliances, nor for portable battery storage devices. + **/ + public static final Integer BATTERY_STORAGE = 24; + /** + * This defines conformance for a Bridged Node root endpoint. This endpoint is akin to a "read me first" + * endpoint that describes itself and any other endpoints that make up the Bridged Node. A Bridged Node endpoint + * represents a device on a foreign network, but is not the root endpoint of the bridge itself. + **/ + public static final Integer BRIDGED_NODE = 19; + /** + * This defines conformance to the Casting Video Client device type. + * A Casting Video Client is a client that can launch content on a Casting Video Player, for example, a Smart + * Speaker or a Content Provider phone app. + **/ + public static final Integer CASTING_VIDEO_CLIENT = 41; + /** + * This defines conformance to the Casting Video Player device type. + * A Video Player (either Basic or Casting) represents a device that is able to play media to a physical output or + * to a display screen which is part of the device. + * A Casting Video Player has basic controls for playback (play, pause, etc.) and keypad input (up, down, number + * input), and is able to launch content. + * For example, a Casting Video Player can be a smart TV device, a TV Set Top Box, or a content streaming device + * that provides input to another device like a TV or computer monitor. + * Please see Video Player Architecture for additional Casting Video Player requirements relating to Video Player + * device endpoint composition, commissioning, feature representation in clusters, and UI context. + **/ + public static final Integer CASTING_VIDEO_PLAYER = 35; + /** + * A Color Dimmer Switch is a controller device that, when bound to a lighting device such as an Extended Color + * Light, is capable of being used to adjust the color of the light being emitted. + **/ + public static final Integer COLOR_DIMMER_SWITCH = 261; + /** + * A Color Temperature Light is a lighting device that is capable of being switched on or off, the intensity of its + * light adjusted, and its color temperature adjusted by means of a bound controller device such as a Color Dimmer + * Switch. + **/ + public static final Integer COLOR_TEMPERATURE_LIGHT = 268; + /** + * This defines conformance to the Contact Sensor device type. + **/ + public static final Integer CONTACT_SENSOR = 21; + /** + * This defines conformance to the Content App device type. + * A Content App is usually an application built by a Content Provider. A Casting Video Player with a Content App + * Platform is able to launch Content Apps and represent these apps as separate endpoints. + **/ + public static final Integer CONTENT_APP = 36; + /** + * A Control Bridge is a controller device that, when bound to a lighting device such as an Extended Color Light, is + * capable of being used to switch the device on or off, adjust the intensity of the light being emitted and adjust + * the color of the light being emitted. In addition, a Control Bridge device is capable of being used for setting + * scenes. + **/ + public static final Integer CONTROL_BRIDGE = 2112; + /** + * A Cook Surface device type represents a heating object on a cooktop or other similar device. It shall only be + * used when composed as part of another device type. + **/ + public static final Integer COOK_SURFACE = 119; + /** + * A cooktop is a cooking surface that heats food either by transferring currents from an electromagnetic field + * located below the glass surface directly to the magnetic induction cookware placed above or through traditional + * gas or electric burners. + **/ + public static final Integer COOKTOP = 120; + /** + * A Device Energy Management device provides reporting and optionally adjustment of the electrical power planned on + * being consumed or produced by the device. + **/ + public static final Integer DEVICE_ENERGY_MANAGEMENT = 1293; + /** + * A Dimmable Light is a lighting device that is capable of being switched on or off and the intensity of its light + * adjusted by means of a bound controller device such as a Dimmer Switch or a Color Dimmer Switch. In addition, a + * Dimmable Light device is also capable of being switched by means of a bound occupancy sensor or other device(s). + **/ + public static final Integer DIMMABLE_LIGHT = 257; + /** + * A Dimmable Plug-In Unit is a device that provides power to another device that is plugged into it, and is capable + * of being switched on or off and have its level adjusted. The Dimmable Plug-in Unit is typically used to control a + * conventional non-communicating light through its mains connection using phase cutting. + **/ + public static final Integer DIMMABLE_PLUG_IN_UNIT = 267; + /** + * A Dimmer Switch is a controller device that, when bound to a lighting device such as a Dimmable Light, is capable + * of being used to switch the device on or off and adjust the intensity of the light being emitted. + **/ + public static final Integer DIMMER_SWITCH = 260; + /** + * A dishwasher is a device that is generally installed in residential homes and is capable of washing dishes, + * cutlery, and other items associate with food preparation and consumption. The device can be permanently installed + * or portable and can have variety of filling and draining methods. + **/ + public static final Integer DISHWASHER = 117; + /** + * A Door Lock Controller is a device capable of controlling a door lock. + **/ + public static final Integer DOOR_LOCK_CONTROLLER = 11; + /** + * A Door Lock is a device used to secure a door. It is possible to actuate a door lock either by means of a manual + * or a remote method. + **/ + public static final Integer DOOR_LOCK = 10; + /** + * An Electrical Sensor device measures the electrical power and/or energy being imported and/or exported. + **/ + public static final Integer ELECTRICAL_SENSOR = 1296; + /** + * An EVSE (Electric Vehicle Supply Equipment) is a device that allows an EV (Electric Vehicle) to be connected to + * the mains electricity supply to allow it to be charged (or discharged in case of Vehicle to Grid / Vehicle to + * Home applications). + **/ + public static final Integer ENERGY_EVSE = 1292; + /** + * An Extended Color Light is a lighting device that is capable of being switched on or off, the intensity of its + * light adjusted, and its color adjusted by means of a bound controller device such as a Color Dimmer Switch or + * Control Bridge. The device supports adjustment of color by means of hue/saturation, enhanced hue, color looping, + * XY coordinates, and color temperature. In addition, the extended color light is also capable of being switched by + * means of a bound occupancy sensor. + **/ + public static final Integer EXTENDED_COLOR_LIGHT = 269; + /** + * An Extractor Hood is a device that is generally installed above a cooking surface in residential kitchens. An + * Extractor Hood’s primary purpose is to reduce odors that arise during the cooking process by either extracting + * the air above the cooking surface or by recirculating and filtering it. It may also contain a light for + * illuminating the cooking surface. + * Extractor Hoods may also be known by the following names: + * • Hoods + * • Extractor Fans + * • Extractors + * • Range Hoods + * • Telescoping Hoods + * • Telescoping Extractors + **/ + public static final Integer EXTRACTOR_HOOD = 122; + /** + * A Fan device is typically standalone or mounted on a ceiling or wall and is used to circulate air in a room. + **/ + public static final Integer FAN = 43; + /** + * A Flow Sensor device measures and reports the flow rate of a fluid. + **/ + public static final Integer FLOW_SENSOR = 774; + /** + * This defines conformance for the Generic Switch device type. + **/ + public static final Integer GENERIC_SWITCH = 15; + /** + * A Heat Pump device is a device that uses electrical energy to heat either spaces or water tanks using ground, + * water or air as the heat source. These typically can heat the air or can pump water via central heating radiators + * or underfloor heating systems. It is typical to also heat hot water and store the heat in a hot water tank. + * Note that the Water Heater device type can also be heated by a heat pump and has similar requirements, but that + * cannot be used for space heating. + **/ + public static final Integer HEAT_PUMP = 777; + /** + * A humidity sensor (in most cases a Relative humidity sensor) reports humidity measurements. + **/ + public static final Integer HUMIDITY_SENSOR = 775; + /** + * A Joint Fabric Administrator device provides capabilities to manage the Joint Fabric Datastore and issue an ICAC + * signed by the Joint Fabric Anchor Root CA. + * A client wanting to access the capabilities of the Joint Fabric Administrator may use the Joint Commissioning + * Method to be commissioned onto the Joint Fabric. Once commissioned, a client may access the capabilities of the + * Joint Fabric Administrator. + **/ + public static final Integer JOINT_FABRIC_ADMINISTRATOR = 304; + /** + * A Laundry Dryer represents a device that is capable of drying laundry items. + **/ + public static final Integer LAUNDRY_DRYER = 124; + /** + * A Laundry Washer represents a device that is capable of laundering consumer items. Any laundry washer product may + * utilize this device type. + * A Laundry Washer shall be composed of at least one endpoint with the Laundry Washer device type. + **/ + public static final Integer LAUNDRY_WASHER = 115; + /** + * A Light Sensor device is a measurement and sensing device that is capable of measuring and reporting the + * intensity of light (illuminance) to which the sensor is being subjected. + **/ + public static final Integer LIGHT_SENSOR = 262; + /** + * This defines conformance to the Microwave Oven device type. + * A Microwave Oven is a device with the primary function of heating foods and beverages using a magnetron. + **/ + public static final Integer MICROWAVE_OVEN = 121; + /** + * This defines conformance to the Mode Select device type. + **/ + public static final Integer MODE_SELECT = 39; + /** + * A Mounted Dimmable Load Control is a fixed device that provides power to another device that is plugged into it, + * and is capable of being switched on or off and have its level adjusted. The Mounted Dimmable Load Control is + * typically used to control a conventional non-communicating light through its mains connection using phase + * cutting. + **/ + public static final Integer MOUNTED_DIMMABLE_LOAD_CONTROL = 272; + /** + * A Mounted On/Off Control is a fixed device that provides power to another device that is plugged into it, and is + * capable of switching that provided power on or off. + **/ + public static final Integer MOUNTED_ON_OFF_CONTROL = 271; + /** + * A Network Infrastructure Manager provides interfaces that allow for the management of the Wi-Fi, Thread, and + * Ethernet networks underlying a Matter deployment, realizing the Star Network Topology described in [MatterCore]. + * Examples of physical devices that implement the Matter Network Infrastructure Manager device type include Wi-Fi + * gateway routers. + * Relevant hardware and software requirements for Network Infrastructure Manager devices are defined in Section + * 15.2.6, “Other Requirements” and within the clusters mandated by this device type. + * A Network Infrastructure Manager device may be managed by a service associated with the device vendor, for + * example, an Internet Service Provider. Sometimes this managing service will have policies that require the use of + * the Managed Device feature of the Access Control Cluster (see Section 15.2.5.1, “Access Control MNGD + * Conformance”). Consequently, Commissioners of this device type should be aware of this feature and its use. + **/ + public static final Integer NETWORK_INFRASTRUCTURE_MANAGER = 144; + /** + * An Occupancy Sensor is a measurement and sensing device that is capable of measuring and reporting the occupancy + * state in a designated area. + **/ + public static final Integer OCCUPANCY_SENSOR = 263; + /** + * The On/Off Light is a lighting device that is capable of being switched on or off by means of a bound controller + * device such as an On/Off Light Switch or a Dimmer Switch. In addition, an on/off light is also capable of being + * switched by means of a bound occupancy sensor. + **/ + public static final Integer ON_OFF_LIGHT = 256; + /** + * An On/Off Light Switch is a controller device that, when bound to a lighting device such as an On/Off Light, is + * capable of being used to switch the device on or off. + **/ + public static final Integer ON_OFF_LIGHT_SWITCH = 259; + /** + * An On/Off Plug-in Unit is a device that provides power to another device that is plugged into it, and is capable + * of switching that provided power on or off. + **/ + public static final Integer ON_OFF_PLUG_IN_UNIT = 266; + /** + * An On/Off Sensor is a measurement and sensing device that, when bound to a lighting device such as a Dimmable + * Light, is capable of being used to switch the device on or off. + **/ + public static final Integer ON_OFF_SENSOR = 2128; + /** + * An OTA Provider is a node that is capable of providing an OTA software update to other nodes on the same fabric. + **/ + public static final Integer OTA_PROVIDER = 20; + /** + * An OTA Requestor is a device that is capable of receiving an OTA software update. + **/ + public static final Integer OTA_REQUESTOR = 18; + /** + * An oven represents a device that contains one or more cabinets, and optionally a single cooktop, that are all + * capable of heating food. Examples of consumer products implementing this device type include ovens, wall ovens, + * convection ovens, etc. + **/ + public static final Integer OVEN = 123; + /** + * + **/ + public static final Integer POWER_SOURCE = 17; + /** + * A Pressure Sensor device measures and reports the pressure of a fluid. + **/ + public static final Integer PRESSURE_SENSOR = 773; + /** + * A Pump Controller device is capable of configuring and controlling a Pump device. + **/ + public static final Integer PUMP_CONTROLLER = 772; + /** + * A Pump device is a pump that may have variable speed. It may have optional built-in sensors and a regulation + * mechanism. It is typically used for pumping fluids like water. + **/ + public static final Integer PUMP = 771; + /** + * This defines conformance to the Rain Sensor device type. + **/ + public static final Integer RAIN_SENSOR = 68; + /** + * A refrigerator represents a device that contains one or more cabinets that are capable of chilling or freezing + * food. Examples of consumer products that may make use of this device type include refrigerators, freezers, and + * wine coolers. + **/ + public static final Integer REFRIGERATOR = 112; + /** + * This defines conformance for the Robotic Vacuum Cleaner device type. + **/ + public static final Integer ROBOTIC_VACUUM_CLEANER = 116; + /** + * This defines conformance to the Room Air Conditioner device type. + * A Room Air Conditioner is a device with the primary function of controlling the air temperature in a single room. + **/ + public static final Integer ROOM_AIR_CONDITIONER = 114; + /** + * This defines conformance for a root node endpoint (see System Model specification). This endpoint is akin to a + * "read me first" endpoint that describes itself and the other endpoints that make up the node. + * • Device types with Endpoint scope shall NOT be supported on the same endpoint as this device type. + * • Clusters with an Application role shall NOT be supported on the same endpoint as this device type. + * • Other device types with Node scope may be supported on the same endpoint as this device type. + **/ + public static final Integer ROOT_NODE = 22; + /** + * A Secondary Network Interface device provides an additional network interface supported by the Node, + * supplementing the primary interface hosted by the Root Node endpoint. + * A Node supporting multiple network interfaces shall include the primary interface on the Root Node endpoint, + * along with secondary interfaces on other endpoints. The priorities of these network interfaces are determined by + * the order of their endpoints, where interfaces with smaller endpoint numbers are higher priority. + **/ + public static final Integer SECONDARY_NETWORK_INTERFACE = 25; + /** + * A Smoke CO Alarm device is capable of sensing smoke, carbon monoxide or both. It is capable of issuing a visual + * and audible alert to indicate elevated concentration of smoke or carbon monoxide. + * Smoke CO Alarms are capable of monitoring themselves and issuing visual and audible alerts for hardware faults, + * critical low battery conditions, and end of service. Optionally, some of the audible alerts can be temporarily + * silenced. Smoke CO Alarms are capable of performing a self-test which performs a diagnostic of the primary sensor + * and issuing a cycle of the audible and visual life safety alarm indications. + * Some smoke alarms may be capable of adjusting sensitivity. Smoke CO Alarm may have the ability to detect and + * report humidity levels, temperature levels, and contamination levels. + **/ + public static final Integer SMOKE_CO_ALARM = 118; + /** + * A Solar Power device is a device that allows a solar panel array, which can optionally be comprised of a set + * parallel strings of solar panels, and its associated controller and, if appropriate, inverter, to be monitored + * and controlled by an Energy Management System. + **/ + public static final Integer SOLAR_POWER = 23; + /** + * This defines conformance to the Speaker device type. This feature controls the speaker volume of the device. + * To control unmute/mute, the On/Off cluster shall be used. A value of TRUE for the OnOff attribute shall represent + * the volume on (not muted) state, while a value of FALSE shall represent the volume off (muted) state. For volume + * level control, the Level cluster shall be used. + * A dedicated endpoint is needed because the On/Off cluster can also be used for other purposes, such as for power + * control. + * The decision to use Level and On/Off clusters for volume (rather than defining a new audio control cluster) was + * made in order to treat volume in a fashion consistent with lighting which also uses these clusters and has + * matching functional requirements. + **/ + public static final Integer SPEAKER = 34; + /** + * A Temperature Controlled Cabinet only exists composed as part of another device type. It represents a single + * cabinet that is capable of having its temperature controlled. Such a cabinet may be chilling or freezing food, + * for example as part of a refrigerator, freezer, wine chiller, or other similar device. Equally, such a cabinet + * may be warming or heating food, for example as part of an oven, range, or similar device. + **/ + public static final Integer TEMPERATURE_CONTROLLED_CABINET = 113; + /** + * A Temperature Sensor device reports measurements of temperature. + **/ + public static final Integer TEMPERATURE_SENSOR = 770; + /** + * A Thermostat device is capable of having either built-in or separate sensors for temperature, humidity or + * occupancy. It allows the desired temperature to be set either remotely or locally. The thermostat is capable of + * sending heating and/or cooling requirement notifications to a heating/cooling unit (for example, an indoor air + * handler) or is capable of including a mechanism to control a heating or cooling unit directly. + **/ + public static final Integer THERMOSTAT = 769; + /** + * This defines conformance to the Video Remote Control device type. + * A Video Remote Control is a client that can control a Video Player, for example, a traditional universal remote + * control. + **/ + public static final Integer VIDEO_REMOTE_CONTROL = 42; + /** + * This defines conformance to the Water Freeze Detector device type. + **/ + public static final Integer WATER_FREEZE_DETECTOR = 65; + /** + * A water heater is a device that is generally installed in properties to heat water for showers, baths etc. + **/ + public static final Integer WATER_HEATER = 1295; + /** + * This defines conformance to the Water Leak Detector device type. + **/ + public static final Integer WATER_LEAK_DETECTOR = 67; + /** + * This defines conformance to the Water Valve device type. + **/ + public static final Integer WATER_VALVE = 66; + /** + * A Window Covering Controller is a device that controls an automatic window covering. + **/ + public static final Integer WINDOW_COVERING_CONTROLLER = 515; + /** + * This defines conformance to the Window Covering device type. + **/ + public static final Integer WINDOW_COVERING = 514; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DiagnosticLogsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DiagnosticLogsCluster.java new file mode 100644 index 00000000000..f472bd926b5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DiagnosticLogsCluster.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * DiagnosticLogs + * + * @author Dan Cunningham - Initial contribution + */ +public class DiagnosticLogsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0032; + public static final String CLUSTER_NAME = "DiagnosticLogs"; + public static final String CLUSTER_PREFIX = "diagnosticLogs"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + + // Enums + public enum IntentEnum implements MatterEnum { + END_USER_SUPPORT(0, "End User Support"), + NETWORK_DIAG(1, "Network Diag"), + CRASH_LOGS(2, "Crash Logs"); + + public final Integer value; + public final String label; + + private IntentEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + EXHAUSTED(1, "Exhausted"), + NO_LOGS(2, "No Logs"), + BUSY(3, "Busy"), + DENIED(4, "Denied"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum TransferProtocolEnum implements MatterEnum { + RESPONSE_PAYLOAD(0, "Response Payload"), + BDX(1, "Bdx"); + + public final Integer value; + public final String label; + + private TransferProtocolEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public DiagnosticLogsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 50, "DiagnosticLogs"); + } + + protected DiagnosticLogsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Reception of this command starts the process of retrieving diagnostic logs from a Node. + */ + public static ClusterCommand retrieveLogsRequest(IntentEnum intent, TransferProtocolEnum requestedProtocol, + String transferFileDesignator) { + Map map = new LinkedHashMap<>(); + if (intent != null) { + map.put("intent", intent); + } + if (requestedProtocol != null) { + map.put("requestedProtocol", requestedProtocol); + } + if (transferFileDesignator != null) { + map.put("transferFileDesignator", transferFileDesignator); + } + return new ClusterCommand("retrieveLogsRequest", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DishwasherAlarmCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DishwasherAlarmCluster.java new file mode 100644 index 00000000000..60ef06a3e7f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DishwasherAlarmCluster.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * DishwasherAlarm + * + * @author Dan Cunningham - Initial contribution + */ +public class DishwasherAlarmCluster extends AlarmBaseCluster { + + public static final int CLUSTER_ID = 0x005D; + public static final String CLUSTER_NAME = "DishwasherAlarm"; + public static final String CLUSTER_PREFIX = "dishwasherAlarm"; + + // Bitmaps + public static class AlarmBitmap { + public boolean inflowError; + public boolean drainError; + public boolean doorError; + public boolean tempTooLow; + public boolean tempTooHigh; + public boolean waterLevelError; + + public AlarmBitmap(boolean inflowError, boolean drainError, boolean doorError, boolean tempTooLow, + boolean tempTooHigh, boolean waterLevelError) { + this.inflowError = inflowError; + this.drainError = drainError; + this.doorError = doorError; + this.tempTooLow = tempTooLow; + this.tempTooHigh = tempTooHigh; + this.waterLevelError = waterLevelError; + } + } + + public DishwasherAlarmCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 93, "DishwasherAlarm"); + } + + protected DishwasherAlarmCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DishwasherModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DishwasherModeCluster.java new file mode 100644 index 00000000000..85b8c4b6cfc --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DishwasherModeCluster.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * DishwasherMode + * + * @author Dan Cunningham - Initial contribution + */ +public class DishwasherModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x0059; + public static final String CLUSTER_NAME = "DishwasherMode"; + public static final String CLUSTER_PREFIX = "dishwasherMode"; + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + NORMAL(16384, "Normal"), + HEAVY(16385, "Heavy"), + LIGHT(16386, "Light"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public DishwasherModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 89, "DishwasherMode"); + } + + protected DishwasherModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DoorLockCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DoorLockCluster.java new file mode 100644 index 00000000000..b243f29d121 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/DoorLockCluster.java @@ -0,0 +1,2008 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * DoorLock + * + * @author Dan Cunningham - Initial contribution + */ +public class DoorLockCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0101; + public static final String CLUSTER_NAME = "DoorLock"; + public static final String CLUSTER_PREFIX = "doorLock"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_LOCK_STATE = "lockState"; + public static final String ATTRIBUTE_LOCK_TYPE = "lockType"; + public static final String ATTRIBUTE_ACTUATOR_ENABLED = "actuatorEnabled"; + public static final String ATTRIBUTE_DOOR_STATE = "doorState"; + public static final String ATTRIBUTE_DOOR_OPEN_EVENTS = "doorOpenEvents"; + public static final String ATTRIBUTE_DOOR_CLOSED_EVENTS = "doorClosedEvents"; + public static final String ATTRIBUTE_OPEN_PERIOD = "openPeriod"; + public static final String ATTRIBUTE_NUMBER_OF_TOTAL_USERS_SUPPORTED = "numberOfTotalUsersSupported"; + public static final String ATTRIBUTE_NUMBER_OF_PIN_USERS_SUPPORTED = "numberOfPinUsersSupported"; + public static final String ATTRIBUTE_NUMBER_OF_RFID_USERS_SUPPORTED = "numberOfRfidUsersSupported"; + public static final String ATTRIBUTE_NUMBER_OF_WEEK_DAY_SCHEDULES_SUPPORTED_PER_USER = "numberOfWeekDaySchedulesSupportedPerUser"; + public static final String ATTRIBUTE_NUMBER_OF_YEAR_DAY_SCHEDULES_SUPPORTED_PER_USER = "numberOfYearDaySchedulesSupportedPerUser"; + public static final String ATTRIBUTE_NUMBER_OF_HOLIDAY_SCHEDULES_SUPPORTED = "numberOfHolidaySchedulesSupported"; + public static final String ATTRIBUTE_MAX_PIN_CODE_LENGTH = "maxPinCodeLength"; + public static final String ATTRIBUTE_MIN_PIN_CODE_LENGTH = "minPinCodeLength"; + public static final String ATTRIBUTE_MAX_RFID_CODE_LENGTH = "maxRfidCodeLength"; + public static final String ATTRIBUTE_MIN_RFID_CODE_LENGTH = "minRfidCodeLength"; + public static final String ATTRIBUTE_CREDENTIAL_RULES_SUPPORT = "credentialRulesSupport"; + public static final String ATTRIBUTE_NUMBER_OF_CREDENTIALS_SUPPORTED_PER_USER = "numberOfCredentialsSupportedPerUser"; + public static final String ATTRIBUTE_LANGUAGE = "language"; + public static final String ATTRIBUTE_LED_SETTINGS = "ledSettings"; + public static final String ATTRIBUTE_AUTO_RELOCK_TIME = "autoRelockTime"; + public static final String ATTRIBUTE_SOUND_VOLUME = "soundVolume"; + public static final String ATTRIBUTE_OPERATING_MODE = "operatingMode"; + public static final String ATTRIBUTE_SUPPORTED_OPERATING_MODES = "supportedOperatingModes"; + public static final String ATTRIBUTE_DEFAULT_CONFIGURATION_REGISTER = "defaultConfigurationRegister"; + public static final String ATTRIBUTE_ENABLE_LOCAL_PROGRAMMING = "enableLocalProgramming"; + public static final String ATTRIBUTE_ENABLE_ONE_TOUCH_LOCKING = "enableOneTouchLocking"; + public static final String ATTRIBUTE_ENABLE_INSIDE_STATUS_LED = "enableInsideStatusLed"; + public static final String ATTRIBUTE_ENABLE_PRIVACY_MODE_BUTTON = "enablePrivacyModeButton"; + public static final String ATTRIBUTE_LOCAL_PROGRAMMING_FEATURES = "localProgrammingFeatures"; + public static final String ATTRIBUTE_WRONG_CODE_ENTRY_LIMIT = "wrongCodeEntryLimit"; + public static final String ATTRIBUTE_USER_CODE_TEMPORARY_DISABLE_TIME = "userCodeTemporaryDisableTime"; + public static final String ATTRIBUTE_SEND_PIN_OVER_THE_AIR = "sendPinOverTheAir"; + public static final String ATTRIBUTE_REQUIRE_PIN_FOR_REMOTE_OPERATION = "requirePinForRemoteOperation"; + public static final String ATTRIBUTE_EXPIRING_USER_TIMEOUT = "expiringUserTimeout"; + public static final String ATTRIBUTE_ALARM_MASK = "alarmMask"; + public static final String ATTRIBUTE_ALIRO_READER_VERIFICATION_KEY = "aliroReaderVerificationKey"; + public static final String ATTRIBUTE_ALIRO_READER_GROUP_IDENTIFIER = "aliroReaderGroupIdentifier"; + public static final String ATTRIBUTE_ALIRO_READER_GROUP_SUB_IDENTIFIER = "aliroReaderGroupSubIdentifier"; + public static final String ATTRIBUTE_ALIRO_EXPEDITED_TRANSACTION_SUPPORTED_PROTOCOL_VERSIONS = "aliroExpeditedTransactionSupportedProtocolVersions"; + public static final String ATTRIBUTE_ALIRO_GROUP_RESOLVING_KEY = "aliroGroupResolvingKey"; + public static final String ATTRIBUTE_ALIRO_SUPPORTED_BLEUWB_PROTOCOL_VERSIONS = "aliroSupportedBleuwbProtocolVersions"; + public static final String ATTRIBUTE_ALIRO_BLE_ADVERTISING_VERSION = "aliroBleAdvertisingVersion"; + public static final String ATTRIBUTE_NUMBER_OF_ALIRO_CREDENTIAL_ISSUER_KEYS_SUPPORTED = "numberOfAliroCredentialIssuerKeysSupported"; + public static final String ATTRIBUTE_NUMBER_OF_ALIRO_ENDPOINT_KEYS_SUPPORTED = "numberOfAliroEndpointKeysSupported"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute may be NULL if the lock hardware does not currently know the status of the locking mechanism. For + * example, a lock may not know the LockState status after a power cycle until the first lock actuation is + * completed. + * The Not Fully Locked value is used by a lock to indicate that the state of the lock is somewhere between Locked + * and Unlocked so it is only partially secured. For example, a deadbolt could be partially extended and not in a + * dead latched state. + */ + public LockStateEnum lockState; // 0 LockStateEnum R V + /** + * Indicates the type of door lock as defined in LockTypeEnum. + */ + public LockTypeEnum lockType; // 1 LockTypeEnum R V + /** + * Indicates if the lock is currently able to (Enabled) or not able to (Disabled) process remote Lock, Unlock, or + * Unlock with Timeout commands. + */ + public Boolean actuatorEnabled; // 2 bool R V + /** + * Indicates the current door state as defined in DoorStateEnum. + * Null only if an internal error prevents the retrieval of the current door state. + */ + public DoorStateEnum doorState; // 3 DoorStateEnum R V + /** + * This attribute shall hold the number of door open events that have occurred since it was last zeroed. + */ + public Integer doorOpenEvents; // 4 uint32 RW VM + /** + * This attribute shall hold the number of door closed events that have occurred since it was last zeroed. + */ + public Integer doorClosedEvents; // 5 uint32 RW VM + /** + * This attribute shall hold the number of minutes the door has been open since the last time it transitioned from + * closed to open. + */ + public Integer openPeriod; // 6 uint16 RW VM + /** + * Indicates the number of total users supported by the lock. + */ + public Integer numberOfTotalUsersSupported; // 17 uint16 R V + /** + * Indicates the number of PIN users supported. + */ + public Integer numberOfPinUsersSupported; // 18 uint16 R V + /** + * Indicates the number of RFID users supported. + */ + public Integer numberOfRfidUsersSupported; // 19 uint16 R V + /** + * Indicates the number of configurable week day schedule supported per user. + */ + public Integer numberOfWeekDaySchedulesSupportedPerUser; // 20 uint8 R V + /** + * Indicates the number of configurable year day schedule supported per user. + */ + public Integer numberOfYearDaySchedulesSupportedPerUser; // 21 uint8 R V + /** + * Indicates the number of holiday schedules supported for the entire door lock device. + */ + public Integer numberOfHolidaySchedulesSupported; // 22 uint8 R V + /** + * Indicates the maximum length in bytes of a PIN Code on this device. + */ + public Integer maxPinCodeLength; // 23 uint8 R V + /** + * Indicates the minimum length in bytes of a PIN Code on this device. + */ + public Integer minPinCodeLength; // 24 uint8 R V + /** + * Indicates the maximum length in bytes of a RFID Code on this device. The value depends on the RFID code range + * specified by the manufacturer, if media anti-collision identifiers (UID) are used as RFID code, a value of 20 + * (equals 10 Byte ISO 14443A UID) is recommended. + */ + public Integer maxRfidCodeLength; // 25 uint8 R V + /** + * Indicates the minimum length in bytes of a RFID Code on this device. The value depends on the RFID code range + * specified by the manufacturer, if media anti-collision identifiers (UID) are used as RFID code, a value of 8 + * (equals 4 Byte ISO 14443A UID) is recommended. + */ + public Integer minRfidCodeLength; // 26 uint8 R V + /** + * This attribute shall contain a bitmap with the bits set for the values of CredentialRuleEnum supported on this + * device. + */ + public CredentialRulesBitmap credentialRulesSupport; // 27 CredentialRulesBitmap R V + /** + * Indicates the number of credentials that could be assigned for each user. + * Depending on the value of NumberOfRFIDUsersSupported and NumberOfPINUsersSupported it may not be possible to + * assign that number of credentials for a user. + * For example, if the device supports only PIN and RFID credential types, NumberOfCredentialsSupportedPerUser is + * set to 10, NumberOfPINUsersSupported is set to 5 and NumberOfRFIDUsersSupported is set to 3, it will not be + * possible to actually assign 10 credentials for a user because maximum number of credentials in the database is 8. + */ + public Integer numberOfCredentialsSupportedPerUser; // 28 uint8 R V + /** + * Indicates the language for the on-screen or audible user interface using a 2- byte language code from ISO-639-1. + */ + public String language; // 33 string R[W] VM + /** + * Indicates the settings for the LED support, as defined by LEDSettingEnum. + */ + public LEDSettingEnum ledSettings; // 34 LEDSettingEnum R[W] VM + /** + * Indicates the number of seconds to wait after unlocking a lock before it automatically locks again. + * 0=disabled. If set, unlock operations from any source will be timed. For one time unlock with timeout use + * the specific command. + */ + public Integer autoRelockTime; // 35 uint32 R[W] VM + /** + * Indicates the sound volume on a door lock as defined by SoundVolumeEnum. + */ + public SoundVolumeEnum soundVolume; // 36 SoundVolumeEnum R[W] VM + /** + * This attribute shall indicate the current operating mode of the lock as defined in OperatingModeEnum. + */ + public OperatingModeEnum operatingMode; // 37 OperatingModeEnum R[W] VM + /** + * This attribute shall contain a bitmap with all operating bits of the OperatingMode attribute supported by the + * lock. All operating modes NOT supported by a lock shall be set to one. The value of the OperatingMode enumeration + * defines the related bit to be set. + */ + public OperatingModesBitmap supportedOperatingModes; // 38 OperatingModesBitmap R V + /** + * Indicates the default configurations as they are physically set on the device (example: hardware dip switch + * setting, etc…) and represents the default setting for some of the attributes within this cluster (for example: + * LED, Auto Lock, Sound Volume, and Operating Mode attributes). + * This is a read-only attribute and is intended to allow clients to determine what changes may need to be made + * without having to query all the included attributes. It may be beneficial for the clients to know what the + * device’s original settings were in the event that the device needs to be restored to factory default settings. + * If the Client device would like to query and modify the door lock server’s operating settings, it SHOULD send + * read and write attribute requests to the specific attributes. + * For example, the Sound Volume attribute default value is Silent Mode. However, it is possible that the current + * Sound Volume is High Volume. Therefore, if the client wants to query/modify the current Sound Volume setting on + * the server, the client SHOULD read/write to the Sound Volume attribute. + */ + public ConfigurationRegisterBitmap defaultConfigurationRegister; // 39 ConfigurationRegisterBitmap R V + /** + * This attribute shall enable/disable local programming on the door lock of certain features (see + * LocalProgrammingFeatures attribute). If this value is set to TRUE then local programming is enabled on the door + * lock for all features. If it is set to FALSE then local programming is disabled on the door lock for those + * features whose bit is set to 0 in the LocalProgrammingFeatures attribute. Local programming shall be enabled by + * default. + */ + public Boolean enableLocalProgramming; // 40 bool R[W] VA + /** + * This attribute shall enable/disable the ability to lock the door lock with a single touch on the door lock. + */ + public Boolean enableOneTouchLocking; // 41 bool RW VM + /** + * This attribute shall enable/disable an inside LED that allows the user to see at a glance if the door is locked. + */ + public Boolean enableInsideStatusLed; // 42 bool RW VM + /** + * This attribute shall enable/disable a button inside the door that is used to put the lock into privacy mode. When + * the lock is in privacy mode it cannot be manipulated from the outside. + */ + public Boolean enablePrivacyModeButton; // 43 bool RW VM + /** + * Indicates the local programming features that will be disabled when EnableLocalProgramming attribute is set to + * False. If a door lock doesn’t support disabling one aspect of local programming it shall return CONSTRAINT_ERROR + * during a write operation of this attribute. If the EnableLocalProgramming attribute is set to True then all local + * programming features shall be enabled regardless of the bits set to 0 in this attribute. + * The features that can be disabled from local programming are defined in LocalProgrammingFeaturesBitmap. + */ + public LocalProgrammingFeaturesBitmap localProgrammingFeatures; // 44 LocalProgrammingFeaturesBitmap R[W] VA + /** + * Indicates the number of incorrect Pin codes or RFID presentment attempts a user is allowed to enter before the + * lock will enter a lockout state. The value of this attribute is compared to all failing forms of credential + * presentation, including Pin codes used in an Unlock Command when RequirePINforRemoteOperation is set to true. + * Valid range is 1-255 incorrect attempts. The lockout state will be for the duration of + * UserCodeTemporaryDisableTime. If the attribute accepts writes and an attempt to write the value 0 is made, the + * device shall respond with CONSTRAINT_ERROR. + * The lock may reset the counter used to track incorrect credential presentations as required by internal logic, + * environmental events, or other reasons. The lock shall reset the counter if a valid credential is presented. + */ + public Integer wrongCodeEntryLimit; // 48 uint8 R[W] VA + /** + * Indicates the number of seconds that the lock shuts down following wrong code entry. Valid range is 1-255 + * seconds. Device can shut down to lock user out for specified amount of time. (Makes it difficult to try and guess + * a PIN for the device.) If the attribute accepts writes and an attempt to write the attribute to 0 is made, the + * device shall respond with CONSTRAINT_ERROR. + */ + public Integer userCodeTemporaryDisableTime; // 49 uint8 R[W] VA + /** + * Indicates the door locks ability to send PINs over the air. If the attribute is True it is ok for the door lock + * server to send PINs over the air. This attribute determines the behavior of the server’s TX operation. If it is + * false, then it is not ok for the device to send PIN in any messages over the air. + * The PIN field within any door lock cluster message shall keep the first octet unchanged and masks the actual code + * by replacing with 0xFF. For example (PIN "1234" ): If the attribute value is True, 0x04 0x31 0x32 0x33 + * 0x34 shall be used in the PIN field in any door lock cluster message payload. If the attribute value is False, + * 0x04 0xFF 0xFF 0xFF 0xFF shall be used. + */ + public Boolean sendPinOverTheAir; // 50 bool R[W] VA + /** + * Indicates if the door lock requires an optional PIN. If this attribute is set to True, the door lock server + * requires that an optional PINs be included in the payload of remote lock operation events like Lock, Unlock, + * Unlock with Timeout and Toggle in order to function. + */ + public Boolean requirePinForRemoteOperation; // 51 bool R[W] VA + /** + * Indicates the number of minutes a PIN, RFID, Fingerprint, or other credential associated with a user of type + * ExpiringUser shall remain valid after its first use before expiring. When the credential expires the UserStatus + * for the corresponding user record shall be set to OccupiedDisabled. + */ + public Integer expiringUserTimeout; // 53 uint16 R[W] VA + /** + * This attribute is only supported if the Alarms cluster is on the same endpoint. The alarm mask is used to turn + * on/off alarms for particular functions. Alarms for an alarm group are enabled if the associated alarm mask bit is + * set. Each bit represents a group of alarms. Entire alarm groups can be turned on or off by setting or clearing + * the associated bit in the alarm mask. + * This mask DOES NOT apply to the Events mechanism of this cluster. + */ + public AlarmMaskBitmap alarmMask; // 64 AlarmMaskBitmap RW VA + /** + * Indicates the verification key component of the Reader’s key pair as defined in [Aliro]. The value, if not null, + * shall be an uncompressed elliptic curve public key as defined in section 2.3.3 of SEC 1. + * Null if no Reader key pair has been configured on the lock. See SetAliroReaderConfig. + */ + public OctetString aliroReaderVerificationKey; // 128 octstr R A + /** + * Indicates the reader_group_identifier as defined in [Aliro]. + * Null if no reader_group_identifier has been configured on the lock. See SetAliroReaderConfig. + */ + public OctetString aliroReaderGroupIdentifier; // 129 octstr R A + /** + * Indicates the reader_group_sub_identifier as defined in [Aliro]. + */ + public OctetString aliroReaderGroupSubIdentifier; // 130 octstr R A + /** + * Indicates the list of protocol versions supported for expedited transactions as defined in [Aliro]. + */ + public List aliroExpeditedTransactionSupportedProtocolVersions; // 131 list R A + /** + * Indicates the Group Resolving Key as defined in [Aliro]. + * Null if no group resolving key has been configured on the lock. See SetAliroReaderConfig. + */ + public OctetString aliroGroupResolvingKey; // 132 octstr R A + /** + * Indicates the list of protocol versions supported for the Bluetooth LE + UWB Access Control Flow as defined in + * [Aliro]. + */ + public List aliroSupportedBleuwbProtocolVersions; // 133 list R A + /** + * Indicates the version of the Bluetooth LE advertisement as defined in [Aliro]. + */ + public Integer aliroBleAdvertisingVersion; // 134 uint8 R A + /** + * Indicates the maximum number of AliroCredentialIssuerKey credentials that can be stored on the lock. + */ + public Integer numberOfAliroCredentialIssuerKeysSupported; // 135 uint16 R V + /** + * Indicates the maximum number of endpoint key credentials that can be stored on the lock. This limit applies to + * the sum of the number of AliroEvictableEndpointKey credentials and the number of AliroNonEvictableEndpointKey + * credentials. + * > [!NOTE] + * > The credential indices used for these two credential types are independent of each other, similar to all + * other credential types. As long as NumberOfAliroEndpointKeysSupported is at least 2 a client could add a + * credential of type AliroEvictableEndpointKey at any index from 1 to NumberOfAliroEndpointKeysSupported and also + * add a credential of type AliroNonEvictableEndpointKey at the same index, and both credentials would exist on the + * server. + */ + public Integer numberOfAliroEndpointKeysSupported; // 136 uint16 R V + // Structs + + /** + * The door lock server provides several alarms which can be sent when there is a critical state on the door lock. + * The alarms available for the door lock server are listed in AlarmCodeEnum. + */ + public class DoorLockAlarm { + /** + * This field shall indicate the alarm code of the event that has happened. + */ + public AlarmCodeEnum alarmCode; // AlarmCodeEnum + + public DoorLockAlarm(AlarmCodeEnum alarmCode) { + this.alarmCode = alarmCode; + } + } + + /** + * The door lock server sends out a DoorStateChange event when the door lock door state changes. + */ + public class DoorStateChange { + /** + * This field shall indicate the new door state for this door event. + */ + public DoorStateEnum doorState; // DoorStateEnum + + public DoorStateChange(DoorStateEnum doorState) { + this.doorState = doorState; + } + } + + /** + * The door lock server sends out a LockOperation event when the event is triggered by the various lock operation + * sources. + * • If the door lock server supports the Unbolt Door command, it shall generate a LockOperation event with + * LockOperationType set to Unlock after an Unbolt Door command succeeds. + * • If the door lock server supports the Unbolting feature and an Unlock Door command is performed, it shall + * generate a LockOperation event with LockOperationType set to Unlatch when the unlatched state is reached and a + * LockOperation event with LockOperationType set to Unlock when the lock successfully completes the unlock → hold + * latch → release latch and return to unlock state operation. + * • If the command fails during holding or releasing the latch but after passing the unlocked state, the door lock + * server shall generate a LockOperationError event with LockOperationType set to Unlatch and a LockOperation event + * with LockOperationType set to Unlock. + * ◦ If it fails before reaching the unlocked state, the door lock server shall generate only a LockOperationError + * event with LockOperationType set to Unlock. + * • Upon manual actuation, a door lock server that supports the Unbolting feature: + * ◦ shall generate a LockOperation event of LockOperationType Unlatch when it is actuated from the outside. + * ◦ may generate a LockOperation event of LockOperationType Unlatch when it is actuated from the inside. + */ + public class LockOperation { + /** + * This field shall indicate the type of the lock operation that was performed. + */ + public LockOperationTypeEnum lockOperationType; // LockOperationTypeEnum + /** + * This field shall indicate the source of the lock operation that was performed. + */ + public OperationSourceEnum operationSource; // OperationSourceEnum + /** + * This field shall indicate the UserIndex who performed the lock operation. This shall be null if there is no + * user index that can be determined for the given operation source. This shall NOT be null if a user index can + * be determined. In particular, this shall NOT be null if the operation was associated with a valid credential. + */ + public Integer userIndex; // uint16 + /** + * This field shall indicate the fabric index of the fabric that performed the lock operation. This shall be + * null if there is no fabric that can be determined for the given operation source. This shall NOT be null if + * the operation source is "Remote". + */ + public Integer fabricIndex; // fabric-idx + /** + * This field shall indicate the Node ID of the node that performed the lock operation. This shall be null if + * there is no Node associated with the given operation source. This shall NOT be null if the operation source + * is "Remote". + */ + public BigInteger sourceNode; // node-id + /** + * This field shall indicate the list of credentials used in performing the lock operation. This shall be null + * if no credentials were involved. + */ + public List credentials; // list + + public LockOperation(LockOperationTypeEnum lockOperationType, OperationSourceEnum operationSource, + Integer userIndex, Integer fabricIndex, BigInteger sourceNode, List credentials) { + this.lockOperationType = lockOperationType; + this.operationSource = operationSource; + this.userIndex = userIndex; + this.fabricIndex = fabricIndex; + this.sourceNode = sourceNode; + this.credentials = credentials; + } + } + + /** + * The door lock server sends out a LockOperationError event when a lock operation fails for various reasons. + */ + public class LockOperationError { + /** + * This field shall indicate the type of the lock operation that was performed. + */ + public LockOperationTypeEnum lockOperationType; // LockOperationTypeEnum + /** + * This field shall indicate the source of the lock operation that was performed. + */ + public OperationSourceEnum operationSource; // OperationSourceEnum + /** + * This field shall indicate the lock operation error triggered when the operation was performed. + */ + public OperationErrorEnum operationError; // OperationErrorEnum + /** + * This field shall indicate the lock UserIndex who performed the lock operation. This shall be null if there is + * no user id that can be determined for the given operation source. + */ + public Integer userIndex; // uint16 + /** + * This field shall indicate the fabric index of the fabric that performed the lock operation. This shall be + * null if there is no fabric that can be determined for the given operation source. This shall NOT be null if + * the operation source is "Remote". + */ + public Integer fabricIndex; // fabric-idx + /** + * This field shall indicate the Node ID of the node that performed the lock operation. This shall be null if + * there is no Node associated with the given operation source. This shall NOT be null if the operation source + * is "Remote". + */ + public BigInteger sourceNode; // node-id + /** + * This field shall indicate the list of credentials used in performing the lock operation. This shall be null + * if no credentials were involved. + */ + public List credentials; // list + + public LockOperationError(LockOperationTypeEnum lockOperationType, OperationSourceEnum operationSource, + OperationErrorEnum operationError, Integer userIndex, Integer fabricIndex, BigInteger sourceNode, + List credentials) { + this.lockOperationType = lockOperationType; + this.operationSource = operationSource; + this.operationError = operationError; + this.userIndex = userIndex; + this.fabricIndex = fabricIndex; + this.sourceNode = sourceNode; + this.credentials = credentials; + } + } + + /** + * The door lock server sends out a LockUserChange event when a lock user, schedule, or credential change has + * occurred. + */ + public class LockUserChange { + /** + * This field shall indicate the lock data type that was changed. + */ + public LockDataTypeEnum lockDataType; // LockDataTypeEnum + /** + * This field shall indicate the data operation performed on the lock data type changed. + */ + public DataOperationTypeEnum dataOperationType; // DataOperationTypeEnum + /** + * This field shall indicate the source of the user data change. + */ + public OperationSourceEnum operationSource; // OperationSourceEnum + /** + * This field shall indicate the lock UserIndex associated with the change (if any). This shall be null if there + * is no specific user associated with the data operation. This shall be 0xFFFE if all users are affected (e.g. + * Clear Users). + */ + public Integer userIndex; // uint16 + /** + * This field shall indicate the fabric index of the fabric that performed the change (if any). This shall be + * null if there is no fabric that can be determined to have caused the change. This shall NOT be null if the + * operation source is "Remote". + */ + public Integer fabricIndex; // fabric-idx + /** + * This field shall indicate the Node ID that performed the change (if any). The Node ID of the node that + * performed the change. This shall be null if there was no Node involved in the change. This shall NOT be null + * if the operation source is "Remote". + */ + public BigInteger sourceNode; // node-id + /** + * This field shall indicate the index of the specific item that was changed (e.g. schedule, PIN, RFID, etc.) in + * the list of items identified by LockDataType. This shall be null if the LockDataType does not correspond to a + * list that can be indexed into (e.g. ProgrammingUser). This shall be 0xFFFE if all indices are affected (e.g. + * ClearPINCode, ClearRFIDCode, ClearWeekDaySchedule, ClearYearDaySchedule, etc.). + */ + public Integer dataIndex; // uint16 + + public LockUserChange(LockDataTypeEnum lockDataType, DataOperationTypeEnum dataOperationType, + OperationSourceEnum operationSource, Integer userIndex, Integer fabricIndex, BigInteger sourceNode, + Integer dataIndex) { + this.lockDataType = lockDataType; + this.dataOperationType = dataOperationType; + this.operationSource = operationSource; + this.userIndex = userIndex; + this.fabricIndex = fabricIndex; + this.sourceNode = sourceNode; + this.dataIndex = dataIndex; + } + } + + /** + * This struct shall indicate the credential types and their corresponding indices (if any) for the event or user + * record. + */ + public class CredentialStruct { + /** + * This field shall indicate the credential field used to authorize the lock operation. + */ + public CredentialTypeEnum credentialType; // CredentialTypeEnum + /** + * This field shall indicate the index of the specific credential used to authorize the lock operation in the + * list of credentials identified by CredentialType (e.g. PIN, RFID, etc.). This field shall be set to 0 if + * CredentialType is ProgrammingPIN or does not correspond to a list that can be indexed into. + */ + public Integer credentialIndex; // uint16 + + public CredentialStruct(CredentialTypeEnum credentialType, Integer credentialIndex) { + this.credentialType = credentialType; + this.credentialIndex = credentialIndex; + } + } + + // Enums + /** + * This enumeration shall indicate the alarm type. + */ + public enum AlarmCodeEnum implements MatterEnum { + LOCK_JAMMED(0, "Lock Jammed"), + LOCK_FACTORY_RESET(1, "Lock Factory Reset"), + LOCK_RADIO_POWER_CYCLED(3, "Lock Radio Power Cycled"), + WRONG_CODE_ENTRY_LIMIT(4, "Wrong Code Entry Limit"), + FRONT_ESCEUTCHEON_REMOVED(5, "Front Esceutcheon Removed"), + DOOR_FORCED_OPEN(6, "Door Forced Open"), + DOOR_AJAR(7, "Door Ajar"), + FORCED_USER(8, "Forced User"); + + public final Integer value; + public final String label; + + private AlarmCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the credential rule that can be applied to a particular user. + */ + public enum CredentialRuleEnum implements MatterEnum { + SINGLE(0, "Single"), + DUAL(1, "Dual"), + TRI(2, "Tri"); + + public final Integer value; + public final String label; + + private CredentialRuleEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the credential type. + */ + public enum CredentialTypeEnum implements MatterEnum { + PROGRAMMING_PIN(0, "Programming Pin"), + PIN(1, "Pin"), + RFID(2, "Rfid"), + FINGERPRINT(3, "Fingerprint"), + FINGER_VEIN(4, "Finger Vein"), + FACE(5, "Face"), + ALIRO_CREDENTIAL_ISSUER_KEY(6, "Aliro Credential Issuer Key"), + ALIRO_EVICTABLE_ENDPOINT_KEY(7, "Aliro Evictable Endpoint Key"), + ALIRO_NON_EVICTABLE_ENDPOINT_KEY(8, "Aliro Non Evictable Endpoint Key"); + + public final Integer value; + public final String label; + + private CredentialTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the data operation performed. + */ + public enum DataOperationTypeEnum implements MatterEnum { + ADD(0, "Add"), + CLEAR(1, "Clear"), + MODIFY(2, "Modify"); + + public final Integer value; + public final String label; + + private DataOperationTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the current door state. + */ + public enum DoorStateEnum implements MatterEnum { + DOOR_OPEN(0, "Door Open"), + DOOR_CLOSED(1, "Door Closed"), + DOOR_JAMMED(2, "Door Jammed"), + DOOR_FORCED_OPEN(3, "Door Forced Open"), + DOOR_UNSPECIFIED_ERROR(4, "Door Unspecified Error"), + DOOR_AJAR(5, "Door Ajar"); + + public final Integer value; + public final String label; + + private DoorStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the data type that is being or has changed. + */ + public enum LockDataTypeEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + PROGRAMMING_CODE(1, "Programming Code"), + USER_INDEX(2, "User Index"), + WEEK_DAY_SCHEDULE(3, "Week Day Schedule"), + YEAR_DAY_SCHEDULE(4, "Year Day Schedule"), + HOLIDAY_SCHEDULE(5, "Holiday Schedule"), + PIN(6, "Pin"), + RFID(7, "Rfid"), + FINGERPRINT(8, "Fingerprint"), + FINGER_VEIN(9, "Finger Vein"), + FACE(10, "Face"), + ALIRO_CREDENTIAL_ISSUER_KEY(11, "Aliro Credential Issuer Key"), + ALIRO_EVICTABLE_ENDPOINT_KEY(12, "Aliro Evictable Endpoint Key"), + ALIRO_NON_EVICTABLE_ENDPOINT_KEY(13, "Aliro Non Evictable Endpoint Key"); + + public final Integer value; + public final String label; + + private LockDataTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the type of Lock operation performed. + */ + public enum LockOperationTypeEnum implements MatterEnum { + LOCK(0, "Lock"), + UNLOCK(1, "Unlock"), + NON_ACCESS_USER_EVENT(2, "Non Access User Event"), + FORCED_USER_EVENT(3, "Forced User Event"), + UNLATCH(4, "Unlatch"); + + public final Integer value; + public final String label; + + private LockOperationTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the error cause of the Lock/Unlock operation performed. + */ + public enum OperationErrorEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + INVALID_CREDENTIAL(1, "Invalid Credential"), + DISABLED_USER_DENIED(2, "Disabled User Denied"), + RESTRICTED(3, "Restricted"), + INSUFFICIENT_BATTERY(4, "Insufficient Battery"); + + public final Integer value; + public final String label; + + private OperationErrorEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the lock operating mode. + * The table below shows the operating mode and which interfaces are enabled, if supported, for each mode. + * Interface Operational: Yes, No or N/A + * > [!NOTE] + * > For modes that disable the remote interface, the door lock shall respond to Lock, Unlock, Toggle, and Unlock + * with Timeout commands with a response status Failure and not take the action requested by those commands. The + * door lock shall NOT disable the radio or otherwise unbind or leave the network. It shall still respond to all + * other commands and requests. + */ + public enum OperatingModeEnum implements MatterEnum { + NORMAL(0, "Normal"), + VACATION(1, "Vacation"), + PRIVACY(2, "Privacy"), + NO_REMOTE_LOCK_UNLOCK(3, "No Remote Lock Unlock"), + PASSAGE(4, "Passage"); + + public final Integer value; + public final String label; + + private OperatingModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate the source of the Lock/Unlock or user change operation performed. + */ + public enum OperationSourceEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + MANUAL(1, "Manual"), + PROPRIETARY_REMOTE(2, "Proprietary Remote"), + KEYPAD(3, "Keypad"), + AUTO(4, "Auto"), + BUTTON(5, "Button"), + SCHEDULE(6, "Schedule"), + REMOTE(7, "Remote"), + RFID(8, "Rfid"), + BIOMETRIC(9, "Biometric"), + ALIRO(10, "Aliro"); + + public final Integer value; + public final String label; + + private OperationSourceEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate what the status is for a specific user ID. + */ + public enum UserStatusEnum implements MatterEnum { + AVAILABLE(0, "Available"), + OCCUPIED_ENABLED(1, "Occupied Enabled"), + OCCUPIED_DISABLED(3, "Occupied Disabled"); + + public final Integer value; + public final String label; + + private UserStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration shall indicate what the type is for a specific user ID. + */ + public enum UserTypeEnum implements MatterEnum { + UNRESTRICTED_USER(0, "Unrestricted User"), + YEAR_DAY_SCHEDULE_USER(1, "Year Day Schedule User"), + WEEK_DAY_SCHEDULE_USER(2, "Week Day Schedule User"), + PROGRAMMING_USER(3, "Programming User"), + NON_ACCESS_USER(4, "Non Access User"), + FORCED_USER(5, "Forced User"), + DISPOSABLE_USER(6, "Disposable User"), + EXPIRING_USER(7, "Expiring User"), + SCHEDULE_RESTRICTED_USER(8, "Schedule Restricted User"), + REMOTE_ONLY_USER(9, "Remote Only User"); + + public final Integer value; + public final String label; + + private UserTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum LockStateEnum implements MatterEnum { + NOT_FULLY_LOCKED(0, "Not Fully Locked"), + LOCKED(1, "Locked"), + UNLOCKED(2, "Unlocked"), + UNLATCHED(3, "Unlatched"); + + public final Integer value; + public final String label; + + private LockStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum LockTypeEnum implements MatterEnum { + DEAD_BOLT(0, "Dead Bolt"), + MAGNETIC(1, "Magnetic"), + OTHER(2, "Other"), + MORTISE(3, "Mortise"), + RIM(4, "Rim"), + LATCH_BOLT(5, "Latch Bolt"), + CYLINDRICAL_LOCK(6, "Cylindrical Lock"), + TUBULAR_LOCK(7, "Tubular Lock"), + INTERCONNECTED_LOCK(8, "Interconnected Lock"), + DEAD_LATCH(9, "Dead Latch"), + DOOR_FURNITURE(10, "Door Furniture"), + EUROCYLINDER(11, "Eurocylinder"); + + public final Integer value; + public final String label; + + private LockTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum LEDSettingEnum implements MatterEnum { + NO_LED_SIGNAL(0, "No Led Signal"), + NO_LED_SIGNAL_ACCESS_ALLOWED(1, "No Led Signal Access Allowed"), + LED_SIGNAL_ALL(2, "Led Signal All"); + + public final Integer value; + public final String label; + + private LEDSettingEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SoundVolumeEnum implements MatterEnum { + SILENT(0, "Silent"), + LOW(1, "Low"), + HIGH(2, "High"), + MEDIUM(3, "Medium"); + + public final Integer value; + public final String label; + + private SoundVolumeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EventTypeEnum implements MatterEnum { + OPERATION(0, "Operation"), + PROGRAMMING(1, "Programming"), + ALARM(2, "Alarm"); + + public final Integer value; + public final String label; + + private EventTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusCodeEnum implements MatterEnum { + DUPLICATE(2, "Duplicate"), + OCCUPIED(3, "Occupied"); + + public final Integer value; + public final String label; + + private StatusCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + /** + * This bitmap shall indicate the days of the week the Week Day schedule applies for. + */ + public static class DaysMaskBitmap { + public boolean sunday; + public boolean monday; + public boolean tuesday; + public boolean wednesday; + public boolean thursday; + public boolean friday; + public boolean saturday; + + public DaysMaskBitmap(boolean sunday, boolean monday, boolean tuesday, boolean wednesday, boolean thursday, + boolean friday, boolean saturday) { + this.sunday = sunday; + this.monday = monday; + this.tuesday = tuesday; + this.wednesday = wednesday; + this.thursday = thursday; + this.friday = friday; + this.saturday = saturday; + } + } + + public static class CredentialRulesBitmap { + public boolean single; + public boolean dual; + public boolean tri; + + public CredentialRulesBitmap(boolean single, boolean dual, boolean tri) { + this.single = single; + this.dual = dual; + this.tri = tri; + } + } + + public static class OperatingModesBitmap { + public boolean normal; + public boolean vacation; + public boolean privacy; + public boolean noRemoteLockUnlock; + public boolean passage; + public short alwaysSet; + + public OperatingModesBitmap(boolean normal, boolean vacation, boolean privacy, boolean noRemoteLockUnlock, + boolean passage, short alwaysSet) { + this.normal = normal; + this.vacation = vacation; + this.privacy = privacy; + this.noRemoteLockUnlock = noRemoteLockUnlock; + this.passage = passage; + this.alwaysSet = alwaysSet; + } + } + + public static class ConfigurationRegisterBitmap { + /** + * The state of local programming functionality + * This bit shall indicate the state related to local programming: + * • 0 = Local programming is disabled + * • 1 = Local programming is enabled + */ + public boolean localProgramming; + /** + * The state of the keypad interface + * This bit shall indicate the state related to keypad interface: + * • 0 = Keypad interface is disabled + * • 1 = Keypad interface is enabled + */ + public boolean keypadInterface; + /** + * The state of the remote interface + * This bit shall indicate the state related to remote interface: + * • 0 = Remote interface is disabled + * • 1 = Remote interface is enabled + */ + public boolean remoteInterface; + /** + * Sound volume is set to Silent value + * This bit shall indicate the state related to sound volume: + * • 0 = Sound volume value is 0 (Silent) + * • 1 = Sound volume value is equal to something other than 0 + */ + public boolean soundVolume; + /** + * Auto relock time it set to 0 + * This bit shall indicate the state related to auto relock time: + * • 0 = Auto relock time value is 0 + * • 1 = Auto relock time value is equal to something other than 0 + */ + public boolean autoRelockTime; + /** + * LEDs is disabled + * This bit shall indicate the state related to LED settings: + * • 0 = LED settings value is 0 (NoLEDSignal) + * • 1 = LED settings value is equal to something other than 0 + */ + public boolean ledSettings; + + public ConfigurationRegisterBitmap(boolean localProgramming, boolean keypadInterface, boolean remoteInterface, + boolean soundVolume, boolean autoRelockTime, boolean ledSettings) { + this.localProgramming = localProgramming; + this.keypadInterface = keypadInterface; + this.remoteInterface = remoteInterface; + this.soundVolume = soundVolume; + this.autoRelockTime = autoRelockTime; + this.ledSettings = ledSettings; + } + } + + public static class LocalProgrammingFeaturesBitmap { + /** + * The state of the ability to add users, credentials or schedules on the device + * This bit shall indicate whether the door lock is able to add Users/Credentials/Schedules locally: + * • 0 = This ability is disabled + * • 1 = This ability is enabled + */ + public boolean addUsersCredentialsSchedules; + /** + * The state of the ability to modify users, credentials or schedules on the device + * This bit shall indicate whether the door lock is able to modify Users/Credentials/Schedules locally: + * • 0 = This ability is disabled + * • 1 = This ability is enabled + */ + public boolean modifyUsersCredentialsSchedules; + /** + * The state of the ability to clear users, credentials or schedules on the device + * This bit shall indicate whether the door lock is able to clear Users/Credentials/Schedules locally: + * • 0 = This ability is disabled + * • 1 = This ability is enabled + */ + public boolean clearUsersCredentialsSchedules; + /** + * The state of the ability to adjust settings on the device + * This bit shall indicate whether the door lock is able to adjust lock settings locally: + * • 0 = This ability is disabled + * • 1 = This ability is enabled + */ + public boolean adjustSettings; + + public LocalProgrammingFeaturesBitmap(boolean addUsersCredentialsSchedules, + boolean modifyUsersCredentialsSchedules, boolean clearUsersCredentialsSchedules, + boolean adjustSettings) { + this.addUsersCredentialsSchedules = addUsersCredentialsSchedules; + this.modifyUsersCredentialsSchedules = modifyUsersCredentialsSchedules; + this.clearUsersCredentialsSchedules = clearUsersCredentialsSchedules; + this.adjustSettings = adjustSettings; + } + } + + public static class AlarmMaskBitmap { + public boolean lockJammed; + public boolean lockFactoryReset; + public boolean lockRadioPowerCycled; + public boolean wrongCodeEntryLimit; + public boolean frontEscutcheonRemoved; + public boolean doorForcedOpen; + + public AlarmMaskBitmap(boolean lockJammed, boolean lockFactoryReset, boolean lockRadioPowerCycled, + boolean wrongCodeEntryLimit, boolean frontEscutcheonRemoved, boolean doorForcedOpen) { + this.lockJammed = lockJammed; + this.lockFactoryReset = lockFactoryReset; + this.lockRadioPowerCycled = lockRadioPowerCycled; + this.wrongCodeEntryLimit = wrongCodeEntryLimit; + this.frontEscutcheonRemoved = frontEscutcheonRemoved; + this.doorForcedOpen = doorForcedOpen; + } + } + + public static class FeatureMap { + /** + * + * If the User Feature is also supported then any PIN Code stored in the lock shall be associated with a User. + * A lock may support multiple credential types so if the User feature is supported the UserType, UserStatus and + * Schedules are all associated with a User index and not directly with a PIN index. A User index may have + * several credentials associated with it. + */ + public boolean pinCredential; + /** + * + * If the User Feature is also supported then any RFID credential stored in the lock shall be associated with a + * User. + * A lock may support multiple credential types so if the User feature is supported the UserType, UserStatus and + * Schedules are all associated with a User index and not directly with a RFID index. A User Index may have + * several credentials associated with it. + */ + public boolean rfidCredential; + /** + * + * Currently the cluster only defines the metadata format for notifications when a fingerprint/ finger vein + * credential is used to access the lock and doesn’t describe how to create fingerprint/finger vein credentials. + * If the Users feature is also supported then the User that a fingerprint/finger vein is associated with can + * also have its UserType, UserStatus and Schedule modified. + * A lock may support multiple credential types so if the User feature is supported the UserType, UserStatus and + * Schedules are all associated with a User index and not directly with a Finger index. A User Index may have + * several credentials associated with it. + */ + public boolean fingerCredentials; + /** + * + * If the User feature is supported then Week Day Schedules are applied to a User and not a credential. + * Week Day Schedules are used to restrict access to a specified time window on certain days of the week. The + * schedule is repeated each week. + * The lock may automatically adjust the UserType when a schedule is created or cleared. + * Support for WeekDayAccessSchedules requires that the lock has the capability of keeping track of local time. + */ + public boolean weekDayAccessSchedules; + /** + * + * If this feature is supported this indicates that the lock has the ability to determine the position of the + * door which is separate from the state of the lock. + */ + public boolean doorPositionSensor; + /** + * + * Currently the cluster only defines the metadata format for notifications when a face recognition, iris, or + * retina credential is used to access the lock and doesn’t describe how to create face recognition, iris, or + * retina credentials. If the Users feature is also supported then the User that a face recognition, iris, or + * retina credential is associated with can also have its UserType, UserStatus and Schedule modified. + * A lock may support multiple credential types so if the User feature is supported the UserType, UserStatus and + * Schedules are all associated with a User and not directly with a credential. + */ + public boolean faceCredentials; + /** + * + * If this feature is supported then the lock supports the ability to verify a credential provided in a + * lock/unlock command. Currently the cluster only supports providing the PIN credential to the lock/unlock + * commands. If this feature is supported then the PIN Credential feature shall also be supported. + */ + public boolean credentialOverTheAirAccess; + /** + * + * If the User Feature is supported then a lock employs a User database. A User within the User database is used + * to associate credentials and schedules to single user record within the lock. This also means the UserType + * and UserStatus fields are associated with a User and not a credential. + */ + public boolean user; + /** + * + * If the User feature is supported then Year Day Schedules are applied to a User and not a credential. Year Day + * Schedules are used to restrict access to a specified date and time window. + * The lock may automatically adjust the UserType when a schedule is created or cleared. + * Support for YearDayAccessSchedules requires that the lock has the capability of keeping track of local time. + */ + public boolean yearDayAccessSchedules; + /** + * + * This feature is used to setup Holiday Schedule in the lock device. A Holiday Schedule sets a start and stop + * end date/time for the lock to use the specified operating mode set by the Holiday Schedule. + * Support for HolidaySchedules requires that the lock has the capability of keeping track of local time. + */ + public boolean holidaySchedules; + /** + * + * Locks that support this feature differentiate between unbolting and unlocking. The Unbolt Door command + * retracts the bolt without pulling the latch. The Unlock Door command fully unlocks the door by retracting the + * bolt and briefly pulling the latch. While the latch is pulled, the lock state changes to Unlatched. Locks + * without unbolting support don’t differentiate between unbolting and unlocking and perform the same operation + * for both commands. + */ + public boolean unbolting; + /** + * + * Locks that support this feature implement the Aliro specification as defined in [Aliro] and support Matter as + * a method for provisioning Aliro credentials. + */ + public boolean aliroProvisioning; + /** + * + * Locks that support this feature implement the Bluetooth LE + UWB Access Control Flow as defined in [Aliro]. + */ + public boolean aliroBleuwb; + + public FeatureMap(boolean pinCredential, boolean rfidCredential, boolean fingerCredentials, + boolean weekDayAccessSchedules, boolean doorPositionSensor, boolean faceCredentials, + boolean credentialOverTheAirAccess, boolean user, boolean yearDayAccessSchedules, + boolean holidaySchedules, boolean unbolting, boolean aliroProvisioning, boolean aliroBleuwb) { + this.pinCredential = pinCredential; + this.rfidCredential = rfidCredential; + this.fingerCredentials = fingerCredentials; + this.weekDayAccessSchedules = weekDayAccessSchedules; + this.doorPositionSensor = doorPositionSensor; + this.faceCredentials = faceCredentials; + this.credentialOverTheAirAccess = credentialOverTheAirAccess; + this.user = user; + this.yearDayAccessSchedules = yearDayAccessSchedules; + this.holidaySchedules = holidaySchedules; + this.unbolting = unbolting; + this.aliroProvisioning = aliroProvisioning; + this.aliroBleuwb = aliroBleuwb; + } + } + + public DoorLockCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 257, "DoorLock"); + } + + protected DoorLockCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command causes the lock device to lock the door. This command includes an optional code for the lock. The + * door lock may require a PIN depending on the value of the RequirePINForRemoteOperation attribute. + */ + public static ClusterCommand lockDoor(OctetString pinCode) { + Map map = new LinkedHashMap<>(); + if (pinCode != null) { + map.put("pinCode", pinCode); + } + return new ClusterCommand("lockDoor", map); + } + + /** + * This command causes the lock device to unlock the door. This command includes an optional code for the lock. The + * door lock may require a code depending on the value of the RequirePINForRemoteOperation attribute. + * > [!NOTE] + * > If the attribute AutoRelockTime is supported the lock will transition to the locked state when the auto + * relock time has expired. + */ + public static ClusterCommand unlockDoor(OctetString pinCode) { + Map map = new LinkedHashMap<>(); + if (pinCode != null) { + map.put("pinCode", pinCode); + } + return new ClusterCommand("unlockDoor", map); + } + + public static ClusterCommand toggle() { + return new ClusterCommand("toggle"); + } + + /** + * This command causes the lock device to unlock the door with a timeout parameter. After the time in seconds + * specified in the timeout field, the lock device will relock itself automatically. This timeout parameter is only + * temporary for this message transition and overrides the default relock time as specified in the AutoRelockTime + * attribute. If the door lock device is not capable of or does not want to support temporary Relock Timeout, it + * SHOULD NOT support this optional command. + */ + public static ClusterCommand unlockWithTimeout(Integer timeout, OctetString pinCode) { + Map map = new LinkedHashMap<>(); + if (timeout != null) { + map.put("timeout", timeout); + } + if (pinCode != null) { + map.put("pinCode", pinCode); + } + return new ClusterCommand("unlockWithTimeout", map); + } + + /** + * Set a PIN Code into the lock. + * Return status is a global status code or a cluster-specific status code from the Status Codes table and shall be + * one of the following values: + */ + public static ClusterCommand setPinCode(Integer userId, UserStatusEnum userStatus, UserTypeEnum userType, + OctetString pin) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + if (userStatus != null) { + map.put("userStatus", userStatus); + } + if (userType != null) { + map.put("userType", userType); + } + if (pin != null) { + map.put("pin", pin); + } + return new ClusterCommand("setPinCode", map); + } + + /** + * Retrieve a PIN Code. + */ + public static ClusterCommand getPinCode(Integer userId) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + return new ClusterCommand("getPinCode", map); + } + + /** + * Clear a PIN code or all PIN codes. + * For each PIN Code cleared whose user doesn’t have a RFID Code or other credential type, then corresponding user + * record’s UserStatus value shall be set to Available, and UserType value shall be set to UnrestrictedUser and all + * schedules shall be cleared. + */ + public static ClusterCommand clearPinCode(Integer pinSlotIndex) { + Map map = new LinkedHashMap<>(); + if (pinSlotIndex != null) { + map.put("pinSlotIndex", pinSlotIndex); + } + return new ClusterCommand("clearPinCode", map); + } + + /** + * Clear out all PINs on the lock. + * > [!NOTE] + * > On the server, the clear all PIN codes command SHOULD have the same effect as the ClearPINCode command with + * respect to the setting of user status, user type and schedules. + */ + public static ClusterCommand clearAllPinCodes() { + return new ClusterCommand("clearAllPinCodes"); + } + + /** + * Set the status of a user ID. + */ + public static ClusterCommand setUserStatus(Integer userId, UserStatusEnum userStatus) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + if (userStatus != null) { + map.put("userStatus", userStatus); + } + return new ClusterCommand("setUserStatus", map); + } + + /** + * Get the status of a user. + */ + public static ClusterCommand getUserStatus(Integer userId) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + return new ClusterCommand("getUserStatus", map); + } + + /** + * Set a weekly repeating schedule for a specified user. + * The associated UserType may be changed to ScheduleRestrictedUser by the lock when a Week Day schedule is set. + * Return status shall be one of the following values: + */ + public static ClusterCommand setWeekDaySchedule(Integer weekDayIndex, Integer userIndex, DaysMaskBitmap daysMask, + Integer startHour, Integer startMinute, Integer endHour, Integer endMinute) { + Map map = new LinkedHashMap<>(); + if (weekDayIndex != null) { + map.put("weekDayIndex", weekDayIndex); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + if (daysMask != null) { + map.put("daysMask", daysMask); + } + if (startHour != null) { + map.put("startHour", startHour); + } + if (startMinute != null) { + map.put("startMinute", startMinute); + } + if (endHour != null) { + map.put("endHour", endHour); + } + if (endMinute != null) { + map.put("endMinute", endMinute); + } + return new ClusterCommand("setWeekDaySchedule", map); + } + + /** + * Retrieve the specific weekly schedule for the specific user. + */ + public static ClusterCommand getWeekDaySchedule(Integer weekDayIndex, Integer userIndex) { + Map map = new LinkedHashMap<>(); + if (weekDayIndex != null) { + map.put("weekDayIndex", weekDayIndex); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + return new ClusterCommand("getWeekDaySchedule", map); + } + + /** + * Clear the specific weekly schedule or all weekly schedules for the specific user. + * Return status shall be one of the following values: + */ + public static ClusterCommand clearWeekDaySchedule(Integer weekDayIndex, Integer userIndex) { + Map map = new LinkedHashMap<>(); + if (weekDayIndex != null) { + map.put("weekDayIndex", weekDayIndex); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + return new ClusterCommand("clearWeekDaySchedule", map); + } + + /** + * Set a time-specific schedule ID for a specified user. + * The associated UserType may be changed to ScheduleRestrictedUser by the lock when a Year Day schedule is set. + * Return status shall be one of the following values: + */ + public static ClusterCommand setYearDaySchedule(Integer yearDayIndex, Integer userIndex, Integer localStartTime, + Integer localEndTime) { + Map map = new LinkedHashMap<>(); + if (yearDayIndex != null) { + map.put("yearDayIndex", yearDayIndex); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + if (localStartTime != null) { + map.put("localStartTime", localStartTime); + } + if (localEndTime != null) { + map.put("localEndTime", localEndTime); + } + return new ClusterCommand("setYearDaySchedule", map); + } + + /** + * Retrieve the specific year day schedule for the specific schedule and user indexes. + */ + public static ClusterCommand getYearDaySchedule(Integer yearDayIndex, Integer userIndex) { + Map map = new LinkedHashMap<>(); + if (yearDayIndex != null) { + map.put("yearDayIndex", yearDayIndex); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + return new ClusterCommand("getYearDaySchedule", map); + } + + /** + * Clears the specific year day schedule or all year day schedules for the specific user. + * Return status shall be one of the following values: + */ + public static ClusterCommand clearYearDaySchedule(Integer yearDayIndex, Integer userIndex) { + Map map = new LinkedHashMap<>(); + if (yearDayIndex != null) { + map.put("yearDayIndex", yearDayIndex); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + return new ClusterCommand("clearYearDaySchedule", map); + } + + /** + * Set the holiday Schedule by specifying local start time and local end time with respect to any Lock Operating + * Mode. + * Return status shall be one of the following values: + */ + public static ClusterCommand setHolidaySchedule(Integer holidayIndex, Integer localStartTime, Integer localEndTime, + OperatingModeEnum operatingMode) { + Map map = new LinkedHashMap<>(); + if (holidayIndex != null) { + map.put("holidayIndex", holidayIndex); + } + if (localStartTime != null) { + map.put("localStartTime", localStartTime); + } + if (localEndTime != null) { + map.put("localEndTime", localEndTime); + } + if (operatingMode != null) { + map.put("operatingMode", operatingMode); + } + return new ClusterCommand("setHolidaySchedule", map); + } + + /** + * Get the holiday schedule for the specified index. + */ + public static ClusterCommand getHolidaySchedule(Integer holidayIndex) { + Map map = new LinkedHashMap<>(); + if (holidayIndex != null) { + map.put("holidayIndex", holidayIndex); + } + return new ClusterCommand("getHolidaySchedule", map); + } + + /** + * Clears the holiday schedule or all holiday schedules. + */ + public static ClusterCommand clearHolidaySchedule(Integer holidayIndex) { + Map map = new LinkedHashMap<>(); + if (holidayIndex != null) { + map.put("holidayIndex", holidayIndex); + } + return new ClusterCommand("clearHolidaySchedule", map); + } + + /** + * Set the user type for a specified user. + * For user type value please refer to User Type Value. + * Return status shall be one of the following values: + */ + public static ClusterCommand setUserType(Integer userId, UserTypeEnum userType) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + if (userType != null) { + map.put("userType", userType); + } + return new ClusterCommand("setUserType", map); + } + + /** + * Retrieve the user type for a specific user. + */ + public static ClusterCommand getUserType(Integer userId) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + return new ClusterCommand("getUserType", map); + } + + /** + * Set an ID for RFID access into the lock. + * Return status is a global status code or a cluster-specific status code from the Status Codes table and shall be + * one of the following values: + */ + public static ClusterCommand setRfidCode(Integer userId, UserStatusEnum userStatus, UserTypeEnum userType, + OctetString rfidCode) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + if (userStatus != null) { + map.put("userStatus", userStatus); + } + if (userType != null) { + map.put("userType", userType); + } + if (rfidCode != null) { + map.put("rfidCode", rfidCode); + } + return new ClusterCommand("setRfidCode", map); + } + + /** + * Retrieve an RFID code. + */ + public static ClusterCommand getRfidCode(Integer userId) { + Map map = new LinkedHashMap<>(); + if (userId != null) { + map.put("userId", userId); + } + return new ClusterCommand("getRfidCode", map); + } + + /** + * Clear an RFID code or all RFID codes. + * For each RFID Code cleared whose user doesn’t have a PIN Code or other credential type, then the corresponding + * user record’s UserStatus value shall be set to Available, and UserType value shall be set to UnrestrictedUser and + * all schedules shall be cleared. + */ + public static ClusterCommand clearRfidCode(Integer rfidSlotIndex) { + Map map = new LinkedHashMap<>(); + if (rfidSlotIndex != null) { + map.put("rfidSlotIndex", rfidSlotIndex); + } + return new ClusterCommand("clearRfidCode", map); + } + + /** + * Clear out all RFIDs on the lock. If you clear all RFID codes and this user didn’t have a PIN code, the user + * status has to be set to "0 Available", the user type has to be set to the default value, and all + * schedules which are supported have to be set to the default values. + */ + public static ClusterCommand clearAllRfidCodes() { + return new ClusterCommand("clearAllRfidCodes"); + } + + /** + * Set user into the lock. + * Fields used for different use cases: + * Return status is a global status code or a cluster-specific status code from the Status Codes table and shall be + * one of the following values: + * • SUCCESS, if setting User was successful. + * • FAILURE, if some unexpected internal error occurred setting User. + * • OCCUPIED, if OperationType is Add and UserIndex points to an occupied slot. + * • INVALID_COMMAND, if one or more fields violate constraints or are invalid or if OperationType is Modify and + * UserIndex points to an available slot. + */ + public static ClusterCommand setUser(DataOperationTypeEnum operationType, Integer userIndex, String userName, + Integer userUniqueId, UserStatusEnum userStatus, UserTypeEnum userType, CredentialRuleEnum credentialRule) { + Map map = new LinkedHashMap<>(); + if (operationType != null) { + map.put("operationType", operationType); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + if (userName != null) { + map.put("userName", userName); + } + if (userUniqueId != null) { + map.put("userUniqueId", userUniqueId); + } + if (userStatus != null) { + map.put("userStatus", userStatus); + } + if (userType != null) { + map.put("userType", userType); + } + if (credentialRule != null) { + map.put("credentialRule", credentialRule); + } + return new ClusterCommand("setUser", map); + } + + /** + * Retrieve user. + * An InvokeResponse command shall be sent with an appropriate error (e.g. FAILURE, INVALID_COMMAND, etc.) as needed + * otherwise the GetUserResponse Command shall be sent implying a status of SUCCESS. + */ + public static ClusterCommand getUser(Integer userIndex) { + Map map = new LinkedHashMap<>(); + if (userIndex != null) { + map.put("userIndex", userIndex); + } + return new ClusterCommand("getUser", map); + } + + /** + * Clears a user or all Users. + * For each user to clear, all associated credentials (e.g. PIN, RFID, fingerprint, etc.) shall be cleared and the + * user entry values shall be reset to their default values (e.g. UserStatus shall be Available, UserType shall be + * UnrestrictedUser) and all associated schedules shall be cleared. + * A LockUserChange event with the provided UserIndex shall be generated after successfully clearing users. + */ + public static ClusterCommand clearUser(Integer userIndex) { + Map map = new LinkedHashMap<>(); + if (userIndex != null) { + map.put("userIndex", userIndex); + } + return new ClusterCommand("clearUser", map); + } + + /** + * Set a credential (e.g. PIN, RFID, Fingerprint, etc.) into the lock for a new user, existing user, or + * ProgrammingUser. + * Fields used for different use cases: + */ + public static ClusterCommand setCredential(DataOperationTypeEnum operationType, CredentialStruct credential, + OctetString credentialData, Integer userIndex, UserStatusEnum userStatus, UserTypeEnum userType) { + Map map = new LinkedHashMap<>(); + if (operationType != null) { + map.put("operationType", operationType); + } + if (credential != null) { + map.put("credential", credential); + } + if (credentialData != null) { + map.put("credentialData", credentialData); + } + if (userIndex != null) { + map.put("userIndex", userIndex); + } + if (userStatus != null) { + map.put("userStatus", userStatus); + } + if (userType != null) { + map.put("userType", userType); + } + return new ClusterCommand("setCredential", map); + } + + /** + * Retrieve the status of a particular credential (e.g. PIN, RFID, Fingerprint, etc.) by index. + * An InvokeResponse command shall be sent with an appropriate error (e.g. FAILURE, INVALID_COMMAND, etc.) as needed + * otherwise the GetCredentialStatusResponse command shall be sent implying a status of SUCCESS. + */ + public static ClusterCommand getCredentialStatus(CredentialStruct credential) { + Map map = new LinkedHashMap<>(); + if (credential != null) { + map.put("credential", credential); + } + return new ClusterCommand("getCredentialStatus", map); + } + + /** + * Clear one, one type, or all credentials except ProgrammingPIN credential. + * Fields used for different use cases: + * For each credential cleared whose user doesn’t have another valid credential, the corresponding user record shall + * be reset back to default values and its UserStatus value shall be set to Available and UserType value shall be + * set to UnrestrictedUser and all schedules shall be cleared. In this case a LockUserChange event shall be + * generated for the user being cleared. + * Return status shall be one of the following values: + */ + public static ClusterCommand clearCredential(CredentialStruct credential) { + Map map = new LinkedHashMap<>(); + if (credential != null) { + map.put("credential", credential); + } + return new ClusterCommand("clearCredential", map); + } + + /** + * This command causes the lock device to unlock the door without pulling the latch. This command includes an + * optional code for the lock. The door lock may require a code depending on the value of the + * RequirePINForRemoteOperation attribute. + * > [!NOTE] + * > If the attribute AutoRelockTime is supported, the lock will transition to the locked state when the auto + * relock time has expired. + */ + public static ClusterCommand unboltDoor(OctetString pinCode) { + Map map = new LinkedHashMap<>(); + if (pinCode != null) { + map.put("pinCode", pinCode); + } + return new ClusterCommand("unboltDoor", map); + } + + /** + * This command allows communicating an Aliro Reader configuration, as defined in [Aliro], to the lock. + */ + public static ClusterCommand setAliroReaderConfig(OctetString signingKey, OctetString verificationKey, + OctetString groupIdentifier, OctetString groupResolvingKey) { + Map map = new LinkedHashMap<>(); + if (signingKey != null) { + map.put("signingKey", signingKey); + } + if (verificationKey != null) { + map.put("verificationKey", verificationKey); + } + if (groupIdentifier != null) { + map.put("groupIdentifier", groupIdentifier); + } + if (groupResolvingKey != null) { + map.put("groupResolvingKey", groupResolvingKey); + } + return new ClusterCommand("setAliroReaderConfig", map); + } + + /** + * This command allows clearing an existing Aliro Reader configuration for the lock. Administrators shall NOT clear + * an Aliro Reader configuration without explicit user permission. + * > [!NOTE] + * > Using this command will revoke the ability of all existing Aliro user devices that have the old verification + * key to interact with the lock. This effect is not restricted to a single fabric or otherwise scoped in any way. + */ + public static ClusterCommand clearAliroReaderConfig() { + return new ClusterCommand("clearAliroReaderConfig"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "lockState : " + lockState + "\n"; + str += "lockType : " + lockType + "\n"; + str += "actuatorEnabled : " + actuatorEnabled + "\n"; + str += "doorState : " + doorState + "\n"; + str += "doorOpenEvents : " + doorOpenEvents + "\n"; + str += "doorClosedEvents : " + doorClosedEvents + "\n"; + str += "openPeriod : " + openPeriod + "\n"; + str += "numberOfTotalUsersSupported : " + numberOfTotalUsersSupported + "\n"; + str += "numberOfPinUsersSupported : " + numberOfPinUsersSupported + "\n"; + str += "numberOfRfidUsersSupported : " + numberOfRfidUsersSupported + "\n"; + str += "numberOfWeekDaySchedulesSupportedPerUser : " + numberOfWeekDaySchedulesSupportedPerUser + "\n"; + str += "numberOfYearDaySchedulesSupportedPerUser : " + numberOfYearDaySchedulesSupportedPerUser + "\n"; + str += "numberOfHolidaySchedulesSupported : " + numberOfHolidaySchedulesSupported + "\n"; + str += "maxPinCodeLength : " + maxPinCodeLength + "\n"; + str += "minPinCodeLength : " + minPinCodeLength + "\n"; + str += "maxRfidCodeLength : " + maxRfidCodeLength + "\n"; + str += "minRfidCodeLength : " + minRfidCodeLength + "\n"; + str += "credentialRulesSupport : " + credentialRulesSupport + "\n"; + str += "numberOfCredentialsSupportedPerUser : " + numberOfCredentialsSupportedPerUser + "\n"; + str += "language : " + language + "\n"; + str += "ledSettings : " + ledSettings + "\n"; + str += "autoRelockTime : " + autoRelockTime + "\n"; + str += "soundVolume : " + soundVolume + "\n"; + str += "operatingMode : " + operatingMode + "\n"; + str += "supportedOperatingModes : " + supportedOperatingModes + "\n"; + str += "defaultConfigurationRegister : " + defaultConfigurationRegister + "\n"; + str += "enableLocalProgramming : " + enableLocalProgramming + "\n"; + str += "enableOneTouchLocking : " + enableOneTouchLocking + "\n"; + str += "enableInsideStatusLed : " + enableInsideStatusLed + "\n"; + str += "enablePrivacyModeButton : " + enablePrivacyModeButton + "\n"; + str += "localProgrammingFeatures : " + localProgrammingFeatures + "\n"; + str += "wrongCodeEntryLimit : " + wrongCodeEntryLimit + "\n"; + str += "userCodeTemporaryDisableTime : " + userCodeTemporaryDisableTime + "\n"; + str += "sendPinOverTheAir : " + sendPinOverTheAir + "\n"; + str += "requirePinForRemoteOperation : " + requirePinForRemoteOperation + "\n"; + str += "expiringUserTimeout : " + expiringUserTimeout + "\n"; + str += "alarmMask : " + alarmMask + "\n"; + str += "aliroReaderVerificationKey : " + aliroReaderVerificationKey + "\n"; + str += "aliroReaderGroupIdentifier : " + aliroReaderGroupIdentifier + "\n"; + str += "aliroReaderGroupSubIdentifier : " + aliroReaderGroupSubIdentifier + "\n"; + str += "aliroExpeditedTransactionSupportedProtocolVersions : " + + aliroExpeditedTransactionSupportedProtocolVersions + "\n"; + str += "aliroGroupResolvingKey : " + aliroGroupResolvingKey + "\n"; + str += "aliroSupportedBleuwbProtocolVersions : " + aliroSupportedBleuwbProtocolVersions + "\n"; + str += "aliroBleAdvertisingVersion : " + aliroBleAdvertisingVersion + "\n"; + str += "numberOfAliroCredentialIssuerKeysSupported : " + numberOfAliroCredentialIssuerKeysSupported + "\n"; + str += "numberOfAliroEndpointKeysSupported : " + numberOfAliroEndpointKeysSupported + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EcosystemInformationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EcosystemInformationCluster.java new file mode 100644 index 00000000000..ba8536abcdf --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EcosystemInformationCluster.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * EcosystemInformation + * + * @author Dan Cunningham - Initial contribution + */ +public class EcosystemInformationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0750; + public static final String CLUSTER_NAME = "EcosystemInformation"; + public static final String CLUSTER_PREFIX = "ecosystemInformation"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_DEVICE_DIRECTORY = "deviceDirectory"; + public static final String ATTRIBUTE_LOCATION_DIRECTORY = "locationDirectory"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This attribute shall contain the list of logical devices represented by a Bridged Node. Most of the time this + * will contain a single entry, but may grow with more complex device compositions (e.g. another bridge.) An empty + * list indicates that the information is not available. + */ + public List deviceDirectory; // 0 list R F M + /** + * This attribute shall contain the list of rooms, areas and groups associated with the DeviceDirectory entries, and + * shall NOT contain locations which are dynamically generated and removed by an ecosystem. (E.g. a location that is + * generated and removed based on the user being home is not permitted. However, an initially generated location + * name that does not quickly change is acceptable.) An empty list indicates that the information is not available. + * LocationDirectory entries shall be removed if there is no DeviceDirectory that references it. + */ + public List locationDirectory; // 1 list R F M + // Structs + + public class EcosystemDeviceStruct { + /** + * This field shall indicate the device’s name, which is provided externally if the user consents. (For example, + * provided by the user in an ecosystem specific interface.) + */ + public String deviceName; // string + /** + * This field shall be present and set if the DeviceName field is present. + * This field shall indicate the timestamp of when the DeviceName was last modified. + */ + public BigInteger deviceNameLastEdit; // epoch-us + /** + * This field shall indicate the endpoint this EcosystemDeviceStruct is associated with on this Bridge. + * This field shall be present and set to a valid endpoint if the device is accessible through the bridge. + */ + public Integer bridgedEndpoint; // endpoint-no + /** + * This field shall indicate the endpoint this EcosystemDeviceStruct is associated with on the original device + * represented by this bridge’s Bridged Node. If this bridge is receiving the device from another bridge, then + * the OriginalEndpoint field value would be the same on both bridges. This field shall be present and set to a + * valid endpoint on the original device if that device is a Matter device. + */ + public Integer originalEndpoint; // endpoint-no + /** + * This field shall indicate all of the DeviceTypes within the DeviceTypeList in the Descriptor Cluster + * associated with this EcosystemDeviceStruct entry. + * This field shall contain a list of valid device type ids. + */ + public List deviceTypes; // list + /** + * This field shall specify the EcosystemLocationStruct entries in the LocationDirectory attribute associated + * with this EcosystemDeviceStruct. + */ + public List uniqueLocationIDs; // list + /** + * This field shall indicate the timestamp of when the UniqueLocationIDs was last modified. + * > [!NOTE] + * > If multiple server instances update the UniqueLocationIDs field at the same time, it is possible one of + * the updates will be missed. This is considered an acceptable limitation to reduce the complexity of the + * design. Since this is meant to be provided from user input, it is unlikely these signals would be happening + * at one time. + */ + public BigInteger uniqueLocationIDsLastEdit; // epoch-us + public Integer fabricIndex; // FabricIndex + + public EcosystemDeviceStruct(String deviceName, BigInteger deviceNameLastEdit, Integer bridgedEndpoint, + Integer originalEndpoint, List deviceTypes, + List uniqueLocationIDs, BigInteger uniqueLocationIDsLastEdit, Integer fabricIndex) { + this.deviceName = deviceName; + this.deviceNameLastEdit = deviceNameLastEdit; + this.bridgedEndpoint = bridgedEndpoint; + this.originalEndpoint = originalEndpoint; + this.deviceTypes = deviceTypes; + this.uniqueLocationIDs = uniqueLocationIDs; + this.uniqueLocationIDsLastEdit = uniqueLocationIDsLastEdit; + this.fabricIndex = fabricIndex; + } + } + + public class EcosystemLocationStruct { + /** + * This field shall indicate a unique identifier for a specific Ecosystem Information Cluster server instance + * representing the location independent of its LocationDescriptor field. + * UniqueLocationID can be used by the client to determine if the change is a relocation of the device or just a + * renaming of the location. + * No guarantees are given for consistency of the ID between server instances. The same location may be + * represented by different IDs on different Ecosystem Information Cluster server instances, so only the history + * from a single server instance should be considered when evaluating a change. + * UniqueLocationID shall be changed when the LocationDescriptor changes from one existing location to another + * location as a result of an external interaction. (For example, the user changes the location assignment.) + * UniqueLocationID shall NOT be changed when the LocationDescriptor changes name, but still represents the same + * location. (For example, the user renames a room.) UniqueLocationID shall be changed when LocationDescriptor + * changes as a result of another Ecosystem Information Cluster server instance changing and the + * UniqueLocationID on the remote server instance also changes. + * UniqueLocationID shall NOT be changed when LocationDescriptor changes as a result of another Ecosystem + * Information Cluster server instance changing and the UniqueLocationID on the remote server instance does not + * change. + */ + public String uniqueLocationId; // string + /** + * This field shall indicate the location (e.g. living room, driveway) and associated metadata that is provided + * externally if the user consents. (For example, provided by the user in an ecosystem specific interface.) + * "Location" in this context is typically used by the user’s grouping into rooms, areas or other + * logical groupings of how devices are used. So a device might be part of multiple such "Locations"s. + */ + public String locationDescriptor; // locationdesc + /** + * This field shall indicate the timestamp of when the LocationDescriptor was last modified. + */ + public BigInteger locationDescriptorLastEdit; // epoch-us + public Integer fabricIndex; // FabricIndex + + public EcosystemLocationStruct(String uniqueLocationId, String locationDescriptor, + BigInteger locationDescriptorLastEdit, Integer fabricIndex) { + this.uniqueLocationId = uniqueLocationId; + this.locationDescriptor = locationDescriptor; + this.locationDescriptorLastEdit = locationDescriptorLastEdit; + this.fabricIndex = fabricIndex; + } + } + + public EcosystemInformationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1872, "EcosystemInformation"); + } + + protected EcosystemInformationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "deviceDirectory : " + deviceDirectory + "\n"; + str += "locationDirectory : " + locationDirectory + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ElectricalEnergyMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ElectricalEnergyMeasurementCluster.java new file mode 100644 index 00000000000..8a7ee7197f9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ElectricalEnergyMeasurementCluster.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ElectricalEnergyMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class ElectricalEnergyMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0091; + public static final String CLUSTER_NAME = "ElectricalEnergyMeasurement"; + public static final String CLUSTER_PREFIX = "electricalEnergyMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ACCURACY = "accuracy"; + public static final String ATTRIBUTE_CUMULATIVE_ENERGY_IMPORTED = "cumulativeEnergyImported"; + public static final String ATTRIBUTE_CUMULATIVE_ENERGY_EXPORTED = "cumulativeEnergyExported"; + public static final String ATTRIBUTE_PERIODIC_ENERGY_IMPORTED = "periodicEnergyImported"; + public static final String ATTRIBUTE_PERIODIC_ENERGY_EXPORTED = "periodicEnergyExported"; + public static final String ATTRIBUTE_CUMULATIVE_ENERGY_RESET = "cumulativeEnergyReset"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the accuracy of energy measurement by this server. The value of the MeasurementType field on this + * MeasurementAccuracyStruct shall be ElectricalEnergy. + */ + public MeasurementAccuracyStruct accuracy; // 0 MeasurementAccuracyStruct R V + /** + * Indicates the most recent measurement of cumulative energy imported by the server over the lifetime of the + * device, and the timestamp of when the measurement was recorded. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the cumulative energy imported cannot currently be determined, a value of null shall be returned. + */ + public EnergyMeasurementStruct cumulativeEnergyImported; // 1 EnergyMeasurementStruct R V + /** + * Indicates the most recent measurement of cumulative energy exported by the server over the lifetime of the + * device, and the timestamp of when the measurement was recorded. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the cumulative energy exported cannot currently be determined, a value of null shall be returned. + */ + public EnergyMeasurementStruct cumulativeEnergyExported; // 2 EnergyMeasurementStruct R V + /** + * Indicates the most recent measurement of energy imported by the server and the period during which it was + * measured. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the periodic energy imported cannot currently be determined, a value of null shall be returned. + */ + public EnergyMeasurementStruct periodicEnergyImported; // 3 EnergyMeasurementStruct R V + /** + * Indicates the most recent measurement of energy exported by the server and the period during which it was + * measured. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the periodic energy exported cannot currently be determined, a value of null shall be returned. + */ + public EnergyMeasurementStruct periodicEnergyExported; // 4 EnergyMeasurementStruct R V + /** + * Indicates when cumulative measurements were most recently zero. + */ + public CumulativeEnergyResetStruct cumulativeEnergyReset; // 5 CumulativeEnergyResetStruct R V + // Structs + + /** + * This event shall be generated when the server takes a snapshot of the cumulative energy imported by the server, + * exported from the server, or both, but not more frequently than the rate mentioned in the description above of + * the related attribute. + */ + public class CumulativeEnergyMeasured { + /** + * This field shall be the value of CumulativeEnergyImported attribute at the timestamp indicated in its + * EndTimestamp field, EndSystime field, or both. + */ + public EnergyMeasurementStruct energyImported; // EnergyMeasurementStruct + /** + * This field shall be the value of CumulativeEnergyExported attribute at the timestamp indicated in its + * EndTimestamp field, EndSystime field, or both. + */ + public EnergyMeasurementStruct energyExported; // EnergyMeasurementStruct + + public CumulativeEnergyMeasured(EnergyMeasurementStruct energyImported, + EnergyMeasurementStruct energyExported) { + this.energyImported = energyImported; + this.energyExported = energyExported; + } + } + + /** + * This event shall be generated when the server reaches the end of a reporting period for imported energy, exported + * energy, or both. + */ + public class PeriodicEnergyMeasured { + /** + * This field shall be the value of PeriodicEnergyImported attribute at the timestamp indicated in its + * EndTimestamp field, EndSystime field, or both. + */ + public EnergyMeasurementStruct energyImported; // EnergyMeasurementStruct + /** + * This field shall be the value of PeriodicEnergyExported attribute at the timestamp indicated in its + * EndTimestamp field, EndSystime field, or both. + */ + public EnergyMeasurementStruct energyExported; // EnergyMeasurementStruct + + public PeriodicEnergyMeasured(EnergyMeasurementStruct energyImported, EnergyMeasurementStruct energyExported) { + this.energyImported = energyImported; + this.energyExported = energyExported; + } + } + + /** + * This struct shall indicate the amount of energy measured during a given measurement period. + * A server which does not have the ability to determine the time in UTC, or has not yet done so, shall use the + * system time fields to specify the measurement period and observation times. + * A server which has determined the time in UTC shall use the timestamp fields to specify the measurement period. + * Such a server may also include the systime fields to indicate how many seconds had passed since boot for a given + * timestamp; this allows for client-side resolution of UTC time for previous reports that only included systime. + */ + public class EnergyMeasurementStruct { + /** + * This field shall be the reported energy. + * If the EnergyMeasurementStruct represents cumulative energy, then this shall represent the cumulative energy + * recorded at either the value of the EndTimestamp field or the value of the EndSystime field, or both. + * If the EnergyMeasurementStruct represents periodic energy, then this shall represent the energy recorded + * during the period specified by either the StartTimestamp and EndTimestamp fields, the period specified by the + * StartSystime and EndSystime fields, or both. + */ + public BigInteger energy; // energy-mWh + /** + * This field shall indicate the timestamp in UTC of the beginning of the period during which the value of the + * Energy field was measured. + * If this EnergyMeasurementStruct represents cumulative energy, this field shall be omitted. + * Otherwise, if the server had determined the time in UTC at or before the beginning of the measurement period, + * this field shall be indicated. + * Otherwise, if the server had not yet determined the time in UTC at or before the beginning of the measurement + * period, or does not have the capability of determining the time in UTC, this field shall be omitted. + */ + public Integer startTimestamp; // epoch-s + /** + * This field shall indicate the timestamp in UTC of the end of the period during which the value of the Energy + * field was measured. + * If the server had determined the time in UTC by the end of the measurement period, this field shall be + * indicated. + * Otherwise, if the server had not yet determined the time in UTC by the end of the measurement period, or does + * not have the capability of determining the time in UTC, this field shall be omitted. + */ + public Integer endTimestamp; // epoch-s + /** + * This field shall indicate the time elapsed since boot at the beginning of the period during which the value + * of the Energy field was measured. + * If this EnergyMeasurementStruct represents cumulative energy, this field shall be omitted. + * Otherwise, if the server had not yet determined the time in UTC at the start of the measurement period, or + * does not have the capability of determining the time in UTC, this field shall be indicated. + * Otherwise, if the server had determined the time in UTC at or before the beginning of the measurement period, + * this field may be omitted; if it is indicated, its value shall be the time elapsed since boot at the UTC time + * indicated in StartTimestamp. + */ + public BigInteger startSystime; // systime-ms + /** + * This field shall indicate the time elapsed since boot at the end of the period during which the value of the + * Energy field was measured. + * If the server had not yet determined the time in UTC by the end of the measurement period, or does not have + * the capability of determining the time in UTC, this field shall be indicated. + * Otherwise, if the server had determined the time in UTC by the end of the measurement period, this field may + * be omitted; if it is indicated, its value shall be the time elapsed since boot at the UTC time indicated in + * EndTimestamp. + */ + public BigInteger endSystime; // systime-ms + + public EnergyMeasurementStruct(BigInteger energy, Integer startTimestamp, Integer endTimestamp, + BigInteger startSystime, BigInteger endSystime) { + this.energy = energy; + this.startTimestamp = startTimestamp; + this.endTimestamp = endTimestamp; + this.startSystime = startSystime; + this.endSystime = endSystime; + } + } + + /** + * This struct shall represent the times at which cumulative measurements were last zero, either due to + * initialization of the device, or an internal reset of the cumulative value. + */ + public class CumulativeEnergyResetStruct { + /** + * This field shall indicate the timestamp in UTC when the value of the Energy field on the + * CumulativeEnergyImported attribute was most recently zero. + * If the server had determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyImported attribute was most recently zero, this field shall be indicated. + * Otherwise, if the server had not yet determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyImported attribute was most recently zero, or does not have the capability of determining the + * time in UTC, this field shall be omitted. + * If the timestamp in UTC when the value of the Energy field on the CumulativeEnergyImported attribute was most + * recently zero cannot currently be determined, a value of null shall be returned. + */ + public Integer importedResetTimestamp; // epoch-s + /** + * This field shall indicate the timestamp in UTC when the value of the Energy field on the + * CumulativeEnergyExported attribute was most recently zero. + * If the server had determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyExported attribute was most recently zero, this field shall be indicated. + * Otherwise, if the server had not yet determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyExported attribute was most recently zero, or does not have the capability of determining the + * time in UTC, this field shall be omitted. + * If the timestamp in UTC when the value of the Energy field on the CumulativeEnergyExported attribute was most + * recently zero cannot currently be determined, a value of null shall be returned. + */ + public Integer exportedResetTimestamp; // epoch-s + /** + * This field shall indicate the time elapsed since boot when the value of the Energy field on the + * CumulativeEnergyImported attribute was most recently zero. + * If the server had not yet determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyImported attribute was most recently zero, or does not have the capability of determining the + * time in UTC, this field shall be indicated. + * Otherwise, if the server had determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyImported attribute was most recently zero, this field may be omitted; if it is indicated, its + * value shall be the time elapsed since boot at the UTC time indicated in ImportedResetTimestamp. + */ + public BigInteger importedResetSystime; // systime-ms + /** + * This field shall indicate the time elapsed since boot when the value of the Energy field on the + * CumulativeEnergyExported attribute was most recently zero. + * If the server had not yet determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyExported attribute was most recently zero, or does not have the capability of determining the + * time in UTC, this field shall be indicated. + * Otherwise, if the server had determined the time in UTC when the value of the Energy field on the + * CumulativeEnergyExported attribute was most recently zero, this field may be omitted; if it is indicated, its + * value shall be the time elapsed since boot at the UTC time indicated in ImportedResetTimestamp. + */ + public BigInteger exportedResetSystime; // systime-ms + + public CumulativeEnergyResetStruct(Integer importedResetTimestamp, Integer exportedResetTimestamp, + BigInteger importedResetSystime, BigInteger exportedResetSystime) { + this.importedResetTimestamp = importedResetTimestamp; + this.exportedResetTimestamp = exportedResetTimestamp; + this.importedResetSystime = importedResetSystime; + this.exportedResetSystime = exportedResetSystime; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * The feature indicates the server is capable of measuring how much energy is imported by the server. + */ + public boolean importedEnergy; + /** + * + * The feature indicates the server is capable of measuring how much energy is exported by the server. + */ + public boolean exportedEnergy; + /** + * + * The feature indicates the server is capable of measuring how much energy has been imported or exported by the + * server over the device’s lifetime. This measurement may start from when a device’s firmware is updated to + * include this feature, when a device’s firmware is updated to correct measurement errors, or when a device is + * factory reset. + */ + public boolean cumulativeEnergy; + /** + * + * The feature indicates the server is capable of measuring how much energy has been imported or exported by the + * server during a certain period of time. The start and end times for measurement periods shall be determined + * by the server, and may represent overlapping periods. + */ + public boolean periodicEnergy; + + public FeatureMap(boolean importedEnergy, boolean exportedEnergy, boolean cumulativeEnergy, + boolean periodicEnergy) { + this.importedEnergy = importedEnergy; + this.exportedEnergy = exportedEnergy; + this.cumulativeEnergy = cumulativeEnergy; + this.periodicEnergy = periodicEnergy; + } + } + + public ElectricalEnergyMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 145, "ElectricalEnergyMeasurement"); + } + + protected ElectricalEnergyMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "accuracy : " + accuracy + "\n"; + str += "cumulativeEnergyImported : " + cumulativeEnergyImported + "\n"; + str += "cumulativeEnergyExported : " + cumulativeEnergyExported + "\n"; + str += "periodicEnergyImported : " + periodicEnergyImported + "\n"; + str += "periodicEnergyExported : " + periodicEnergyExported + "\n"; + str += "cumulativeEnergyReset : " + cumulativeEnergyReset + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ElectricalPowerMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ElectricalPowerMeasurementCluster.java new file mode 100644 index 00000000000..a5fb185c31b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ElectricalPowerMeasurementCluster.java @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ElectricalPowerMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class ElectricalPowerMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0090; + public static final String CLUSTER_NAME = "ElectricalPowerMeasurement"; + public static final String CLUSTER_PREFIX = "electricalPowerMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_POWER_MODE = "powerMode"; + public static final String ATTRIBUTE_NUMBER_OF_MEASUREMENT_TYPES = "numberOfMeasurementTypes"; + public static final String ATTRIBUTE_ACCURACY = "accuracy"; + public static final String ATTRIBUTE_RANGES = "ranges"; + public static final String ATTRIBUTE_VOLTAGE = "voltage"; + public static final String ATTRIBUTE_ACTIVE_CURRENT = "activeCurrent"; + public static final String ATTRIBUTE_REACTIVE_CURRENT = "reactiveCurrent"; + public static final String ATTRIBUTE_APPARENT_CURRENT = "apparentCurrent"; + public static final String ATTRIBUTE_ACTIVE_POWER = "activePower"; + public static final String ATTRIBUTE_REACTIVE_POWER = "reactivePower"; + public static final String ATTRIBUTE_APPARENT_POWER = "apparentPower"; + public static final String ATTRIBUTE_RMS_VOLTAGE = "rmsVoltage"; + public static final String ATTRIBUTE_RMS_CURRENT = "rmsCurrent"; + public static final String ATTRIBUTE_RMS_POWER = "rmsPower"; + public static final String ATTRIBUTE_FREQUENCY = "frequency"; + public static final String ATTRIBUTE_HARMONIC_CURRENTS = "harmonicCurrents"; + public static final String ATTRIBUTE_HARMONIC_PHASES = "harmonicPhases"; + public static final String ATTRIBUTE_POWER_FACTOR = "powerFactor"; + public static final String ATTRIBUTE_NEUTRAL_CURRENT = "neutralCurrent"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This shall indicate the current mode of the server. For some servers, such as an EV, this may change depending on + * the mode of charging or discharging. + */ + public PowerModeEnum powerMode; // 0 PowerModeEnum R V + /** + * This shall indicate the maximum number of measurement types the server is capable of reporting. + */ + public Integer numberOfMeasurementTypes; // 1 uint8 R V + /** + * This shall indicate a list of accuracy specifications for the measurement types supported by the server. There + * shall be an entry for ActivePower, as well as any other measurement types implemented by this server. + */ + public List accuracy; // 2 list R V + /** + * This shall indicate a list of measured ranges for different measurement types. Each measurement type shall have + * at most one entry in this list, representing the range of measurements in the most recent measurement period. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + */ + public List ranges; // 3 list R V + /** + * This shall indicate the most recent Voltage reading in millivolts (mV). + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the voltage cannot be measured, a value of null shall be returned. + */ + public BigInteger voltage; // 4 voltage-mV R V + /** + * This shall indicate the most recent ActiveCurrent reading in milliamps (mA). + * A positive value represents current flowing into the server, while a negative value represents current flowing + * out of the server. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the current cannot be measured, a value of null shall be returned. + */ + public BigInteger activeCurrent; // 5 amperage-mA R V + /** + * This shall indicate the most recent ReactiveCurrent reading in milliamps (mA). + * A positive value represents current flowing into the server, while a negative value represents current flowing + * out of the server. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the current cannot be measured, a value of null shall be returned. + */ + public BigInteger reactiveCurrent; // 6 amperage-mA R V + /** + * This shall indicate the most recent ApparentCurrent (square root sum of the squares of active and reactive + * currents) reading in milliamps (mA). + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the active or reactive currents cannot be measured, a value of null shall be returned. + */ + public BigInteger apparentCurrent; // 7 amperage-mA R V + /** + * This shall indicate the most recent ActivePower reading in milliwatts (mW). If the power cannot be measured, a + * value of null shall be returned. + * A positive value represents power imported, while a negative value represents power exported. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the Polyphase Power feature is set, this value represents the combined active power imported or exported. + */ + public BigInteger activePower; // 8 power-mW R V + /** + * This shall indicate the most recent ReactivePower reading in millivolt-amps reactive (mVAR). A positive value + * represents power imported, while a negative value represents power exported. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the reactive power cannot be measured, a value of null shall be returned. + * If the Polyphase Power feature is supported, this value represents the combined reactive power imported or + * exported. + */ + public BigInteger reactivePower; // 9 power-mW R V + /** + * This shall indicate the most recent ApparentPower reading in millivolt-amps (mVA). + * A positive value represents power imported, while a negative value represents power exported. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the apparent power cannot be measured, a value of null shall be returned. + */ + public BigInteger apparentPower; // 10 power-mW R V + /** + * This shall indicate the most recent RMSVoltage reading in millivolts (mV). + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the RMS voltage cannot be measured, a value of null shall be returned. + */ + public BigInteger rmsVoltage; // 11 voltage-mV R V + /** + * This shall indicate the most recent RMSCurrent reading in milliamps (mA). + * A positive value represents current flowing into the server, while a negative value represents current flowing + * out of the server. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the RMS current cannot be measured, a value of null shall be returned. + */ + public BigInteger rmsCurrent; // 12 amperage-mA R V + /** + * This shall indicate the most recent RMSPower reading in milliwatts (mW). + * A positive value represents power imported, while a negative value represents power exported. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the RMS power cannot be measured, a value of null shall be returned. + */ + public BigInteger rmsPower; // 13 power-mW R V + /** + * This shall indicate the most recent Frequency reading in millihertz (mHz). + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + * If the frequency cannot be measured, a value of null shall be returned. + */ + public BigInteger frequency; // 14 int64 R V + /** + * This shall indicate a list of HarmonicMeasurementStruct values, with each HarmonicMeasurementStruct representing + * the harmonic current reading for the harmonic order specified by Order. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + */ + public List harmonicCurrents; // 15 list R V + /** + * This shall indicate a list of HarmonicMeasurementStruct values, with each HarmonicMeasurementStruct representing + * the most recent phase of the harmonic current reading for the harmonic order specified by Order. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + */ + public List harmonicPhases; // 16 list R V + /** + * This shall indicate the Power Factor ratio in +/- 1/100ths of a percent. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + */ + public BigInteger powerFactor; // 17 int64 R V + /** + * This shall indicate the most recent NeutralCurrent reading in milliamps (mA). Typically this is a derived value, + * taking the magnitude of the vector sum of phase currents. + * If the neutral current cannot be measured or derived, a value of null shall be returned. + * A positive value represents an imbalance between the phase currents when power is imported. + * A negative value represents an imbalance between the phase currents when power is exported. + * The reporting interval of this attribute shall be manufacturer dependent. The server may choose to omit + * publication of deltas considered not meaningful. + * The server shall NOT mark this attribute ready for report if the last time this was done was more recently than 1 + * second ago. + * The server may delay marking this attribute ready for report for longer periods if needed, however the server + * shall NOT delay marking this attribute as ready for report for longer than 60 seconds. + */ + public BigInteger neutralCurrent; // 18 amperage-mA R V + // Structs + + /** + * If supported, this event shall be generated at the end of a measurement period. The start and end times for + * measurement periods shall be determined by the server, and may represent overlapping periods. + */ + public class MeasurementPeriodRanges { + /** + * This shall indicate the value of the Ranges attribute at the time of event generation. + */ + public List ranges; // list + + public MeasurementPeriodRanges(List ranges) { + this.ranges = ranges; + } + } + + /** + * This struct shall indicate the maximum and minimum values of a given measurement type during a measurement + * period, along with the observation times of these values. + * A server which does not have the ability to determine the time in UTC, or has not yet done so, shall use the + * system time fields to specify the measurement period and observation times. + * A server which has determined the time in UTC shall use the timestamp fields to specify the measurement period + * and observation times. Such a server may also include the systime fields to indicate how many seconds had passed + * since boot for a given timestamp; this allows for client-side resolution of UTC time for previous reports that + * only included systime. + */ + public class MeasurementRangeStruct { + /** + * This field shall be the type of measurement for the range provided. + */ + public MeasurementTypeEnum measurementType; // MeasurementTypeEnum + /** + * This field shall be the smallest measured value for the associated measurement over either the period between + * StartTimestamp and EndTimestamp, or the period between StartSystime and EndSystime, or both. + */ + public BigInteger min; // int64 + /** + * This field shall be the largest measured value for the associated measurement over the period between either + * StartTimestamp and EndTimestamp or the period between StartSystime and EndSystime, or both. + */ + public BigInteger max; // int64 + /** + * This field shall be the timestamp in UTC of the beginning of the measurement period. + * If the server had not yet determined the time in UTC at or before the beginning of the measurement period, or + * does not have the capability of determining the time in UTC, this field shall be omitted. + */ + public Integer startTimestamp; // epoch-s + /** + * This field shall be the timestamp in UTC of the end of the measurement period. + * If the server had not yet determined the time in UTC at or before the beginning of the measurement period, or + * does not have the capability of determining the time in UTC, this field shall be omitted. + */ + public Integer endTimestamp; // epoch-s + /** + * This field shall be the most recent timestamp in UTC that the value in the Min field was measured. + * This field shall be greater than or equal to the value of the StartTimestamp field. This field shall be less + * than or equal to the value of the EndTimestamp field. + */ + public Integer minTimestamp; // epoch-s + /** + * This field shall be the most recent timestamp in UTC of the value in the Max field. This field shall be + * greater than or equal to the value of the StartTimestamp field. This field shall be less than or equal to the + * value of the EndTimestamp field. + */ + public Integer maxTimestamp; // epoch-s + /** + * This field shall be the time since boot of the beginning of the measurement period. + * If the server had determined the time in UTC at or before the start of the measurement period, this field may + * be omitted along with the EndSystime, MinSystime, and MaxSystime fields. + */ + public BigInteger startSystime; // systime-ms + /** + * This field shall be the time since boot of the end of the measurement period. + * If the server had determined the time in UTC at the end of the measurement period, this field may be omitted + * along with the StartSystime field, MinSystime, and MaxSystime fields. + */ + public BigInteger endSystime; // systime-ms + /** + * This field shall be the measurement time since boot of the value in the Min field was measured. This field + * shall be greater than or equal to the value of the StartSystime field. + * This field shall be less than or equal to the value of the EndSystime field. + */ + public BigInteger minSystime; // systime-ms + /** + * This field shall be the measurement time since boot of the value in the Max field. This field shall be + * greater than or equal to the value of the StartSystime field. + * This field shall be less than or equal to the value of the EndSystime field. + */ + public BigInteger maxSystime; // systime-ms + + public MeasurementRangeStruct(MeasurementTypeEnum measurementType, BigInteger min, BigInteger max, + Integer startTimestamp, Integer endTimestamp, Integer minTimestamp, Integer maxTimestamp, + BigInteger startSystime, BigInteger endSystime, BigInteger minSystime, BigInteger maxSystime) { + this.measurementType = measurementType; + this.min = min; + this.max = max; + this.startTimestamp = startTimestamp; + this.endTimestamp = endTimestamp; + this.minTimestamp = minTimestamp; + this.maxTimestamp = maxTimestamp; + this.startSystime = startSystime; + this.endSystime = endSystime; + this.minSystime = minSystime; + this.maxSystime = maxSystime; + } + } + + public class HarmonicMeasurementStruct { + /** + * This field shall be the order of the harmonic being measured. Typically this is an odd number, but servers + * may choose to report even harmonics. + */ + public Integer order; // uint8 + /** + * This field shall be the measured value for the given harmonic order. + * For the Harmonic Currents attribute, this value is the most recently measured harmonic current reading in + * milliamps (mA). A positive value indicates that the measured harmonic current is positive, and a negative + * value indicates that the measured harmonic current is negative. + * For the Harmonic Phases attribute, this value is the most recent phase of the given harmonic order in + * millidegrees (mDeg). A positive value indicates that the measured phase is leading, and a negative value + * indicates that the measured phase is lagging. + * If this measurement is not currently available, a value of null shall be returned. + */ + public BigInteger measurement; // int64 + + public HarmonicMeasurementStruct(Integer order, BigInteger measurement) { + this.order = order; + this.measurement = measurement; + } + } + + // Enums + public enum PowerModeEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + DC(1, "Dc"), + AC(2, "Ac"); + + public final Integer value; + public final String label; + + private PowerModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature indicates the cluster can measure a direct current. + */ + public boolean directCurrent; + /** + * + * This feature indicates the cluster can measure an alternating current. + */ + public boolean alternatingCurrent; + /** + * + * This feature indicates the cluster represents the collective measurements for a Polyphase power supply. + */ + public boolean polyphasePower; + /** + * + * This feature indicates the cluster can measure the harmonics of an alternating current. + */ + public boolean harmonics; + /** + * + * This feature indicates the cluster can measure the harmonic phases of an alternating current. + */ + public boolean powerQuality; + + public FeatureMap(boolean directCurrent, boolean alternatingCurrent, boolean polyphasePower, boolean harmonics, + boolean powerQuality) { + this.directCurrent = directCurrent; + this.alternatingCurrent = alternatingCurrent; + this.polyphasePower = polyphasePower; + this.harmonics = harmonics; + this.powerQuality = powerQuality; + } + } + + public ElectricalPowerMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 144, "ElectricalPowerMeasurement"); + } + + protected ElectricalPowerMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "powerMode : " + powerMode + "\n"; + str += "numberOfMeasurementTypes : " + numberOfMeasurementTypes + "\n"; + str += "accuracy : " + accuracy + "\n"; + str += "ranges : " + ranges + "\n"; + str += "voltage : " + voltage + "\n"; + str += "activeCurrent : " + activeCurrent + "\n"; + str += "reactiveCurrent : " + reactiveCurrent + "\n"; + str += "apparentCurrent : " + apparentCurrent + "\n"; + str += "activePower : " + activePower + "\n"; + str += "reactivePower : " + reactivePower + "\n"; + str += "apparentPower : " + apparentPower + "\n"; + str += "rmsVoltage : " + rmsVoltage + "\n"; + str += "rmsCurrent : " + rmsCurrent + "\n"; + str += "rmsPower : " + rmsPower + "\n"; + str += "frequency : " + frequency + "\n"; + str += "harmonicCurrents : " + harmonicCurrents + "\n"; + str += "harmonicPhases : " + harmonicPhases + "\n"; + str += "powerFactor : " + powerFactor + "\n"; + str += "neutralCurrent : " + neutralCurrent + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyEvseCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyEvseCluster.java new file mode 100644 index 00000000000..ad0a18b9110 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyEvseCluster.java @@ -0,0 +1,834 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * EnergyEvse + * + * @author Dan Cunningham - Initial contribution + */ +public class EnergyEvseCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0099; + public static final String CLUSTER_NAME = "EnergyEvse"; + public static final String CLUSTER_PREFIX = "energyEvse"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_STATE = "state"; + public static final String ATTRIBUTE_SUPPLY_STATE = "supplyState"; + public static final String ATTRIBUTE_FAULT_STATE = "faultState"; + public static final String ATTRIBUTE_CHARGING_ENABLED_UNTIL = "chargingEnabledUntil"; + public static final String ATTRIBUTE_DISCHARGING_ENABLED_UNTIL = "dischargingEnabledUntil"; + public static final String ATTRIBUTE_CIRCUIT_CAPACITY = "circuitCapacity"; + public static final String ATTRIBUTE_MINIMUM_CHARGE_CURRENT = "minimumChargeCurrent"; + public static final String ATTRIBUTE_MAXIMUM_CHARGE_CURRENT = "maximumChargeCurrent"; + public static final String ATTRIBUTE_MAXIMUM_DISCHARGE_CURRENT = "maximumDischargeCurrent"; + public static final String ATTRIBUTE_USER_MAXIMUM_CHARGE_CURRENT = "userMaximumChargeCurrent"; + public static final String ATTRIBUTE_RANDOMIZATION_DELAY_WINDOW = "randomizationDelayWindow"; + public static final String ATTRIBUTE_NEXT_CHARGE_START_TIME = "nextChargeStartTime"; + public static final String ATTRIBUTE_NEXT_CHARGE_TARGET_TIME = "nextChargeTargetTime"; + public static final String ATTRIBUTE_NEXT_CHARGE_REQUIRED_ENERGY = "nextChargeRequiredEnergy"; + public static final String ATTRIBUTE_NEXT_CHARGE_TARGET_SO_C = "nextChargeTargetSoC"; + public static final String ATTRIBUTE_APPROXIMATE_EV_EFFICIENCY = "approximateEvEfficiency"; + public static final String ATTRIBUTE_STATE_OF_CHARGE = "stateOfCharge"; + public static final String ATTRIBUTE_BATTERY_CAPACITY = "batteryCapacity"; + public static final String ATTRIBUTE_VEHICLE_ID = "vehicleId"; + public static final String ATTRIBUTE_SESSION_ID = "sessionId"; + public static final String ATTRIBUTE_SESSION_DURATION = "sessionDuration"; + public static final String ATTRIBUTE_SESSION_ENERGY_CHARGED = "sessionEnergyCharged"; + public static final String ATTRIBUTE_SESSION_ENERGY_DISCHARGED = "sessionEnergyDischarged"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the current status of the EVSE. This higher-level status is partly derived from the signaling protocol + * as communicated between the EVSE and the vehicle through the pilot signal. + * The State attribute shall change when the EVSE detects change of condition of the EV (plugged in or unplugged, + * whether the vehicle is asking for demand or not, and if it is charging or discharging). + * > [!NOTE] + * > SessionEnding is not really a state but a transition. However, the transition period may take a few seconds + * and is useful for some clean up purposes. + * The Fault state is used to indicate that the FaultState attribute is not NoError. A null value shall indicate + * that the state cannot be determined. + */ + public StateEnum state; // 0 StateEnum R V + /** + * Indicates whether the EV is currently allowed to charge from or discharge to the EVSE. + */ + public SupplyStateEnum supplyState; // 1 SupplyStateEnum R V + /** + * Indicates the type of fault detected by the EVSE (internally or as detected in the pilot signal). + * When the SupplyState attribute is DisabledError, the FaultState attribute will be one of the values listed in + * FaultStateEnum, except NoError. For all values of SupplyState other than DisabledError, the FaultState attribute + * shall be NoError. + */ + public FaultStateEnum faultState; // 2 FaultStateEnum R V + /** + * Indicates the time, in UTC, that the EVSE will automatically stop current flow to the EV. + * A null value indicates the EVSE is always enabled for charging. + * A value in the past or 0x0 indicates that EVSE charging shall be disabled. The attribute is only set via the + * payload of the EnableCharging command. + * This attribute shall be persisted, for example a temporary power failure should not stop the vehicle from being + * charged. + */ + public Integer chargingEnabledUntil; // 3 epoch-s R V + /** + * Indicates the time, in UTC, that the EVSE will automatically stop current flow from the EV. + * A null value indicates the EVSE is always enabled for discharging. + * A value in the past or 0x0 indicates that EVSE discharging shall be disabled. The attribute is only set via the + * payload of the EnableDischarging command. + * This attribute shall be persisted, for example a temporary power failure should not stop the vehicle from being + * discharged. + */ + public Integer dischargingEnabledUntil; // 4 epoch-s R V + /** + * Indicates the capacity that the circuit that the EVSE is connected to can provide. It is intended to allow + * implementation of a self-managed network of EVSEs. It is assumed that the device will allow the setting of such + * values by an installer. + */ + public BigInteger circuitCapacity; // 5 amperage-mA R V + /** + * Indicates the minimum current that can be delivered by the EVSE to the EV. The attribute can be set using the + * EnableCharging command. + */ + public BigInteger minimumChargeCurrent; // 6 amperage-mA R V + /** + * Indicates the maximum current that can be delivered by the EVSE to the EV. + * This shall represent the actual maximum current offered to the EV at any time. Note that the EV can draw less + * current than this value. For example, the EV may be limiting its power draw based on the operating conditions of + * the battery, such as temperature and state of charge. + * The attribute can be initially set using the EnableCharging command or by adjusting the UserMaximumChargeCurrent + * attribute. + * This attribute value shall be the minimum of: + * • CircuitCapacity - Electrician’s installation setting + * • CableAssemblyCurrentLimit (detected by the EVSE when the cable is plugged in) + * • MaximumChargeCurrent field in the EnableCharging command + * • UserMaximumChargeCurrent attribute + */ + public BigInteger maximumChargeCurrent; // 7 amperage-mA R V + /** + * Indicates the maximum current that can be received by the EVSE from the EV. This attribute can be set using the + * EnableDischarging command. + * This attribute value shall be the minimum of: + * • CircuitCapacity - Electrician’s installation setting + * • CableAssemblyCurrentLimit (detected by the EVSE when the cable is plugged in) + * • MaximumDischargeCurrent field in the EnableDischarging command + */ + public BigInteger maximumDischargeCurrent; // 8 amperage-mA R V + /** + * Indicates a maximum current that can set by the consumer (e.g. via an app) as a preference to further reduce the + * charging rate. This may be desirable if the home owner has a solar PV or battery storage system which may only be + * able to deliver a limited amount of power. The consumer can manually control how much they allow the EV to take. + * This attribute value shall be limited by the EVSE to be in the range of: + * MinimumChargeCurrent <= UserMaximumChargeCurrent <= MaximumChargeCurrent where + * MinimumChargeCurrent and MaximumChargeCurrent are the values received in the EnableCharging command. + * Its default value SHOULD be initialized to the same as the CircuitCapacity attribute. This value shall be + * persisted across reboots to ensure it does not cause charging issues during temporary power failures. + */ + public BigInteger userMaximumChargeCurrent; // 9 amperage-mA RW VM + /** + * Indicates the size of a random window over which the EVSE will randomize the start of a charging session. This + * value is in seconds. + * This is a feature that is mandated in some markets (such as UK) where the EVSE should by default randomize its + * start time within the randomization window. By default in the UK this should be 600s. + * For example, if the RandomizationDelayWindow is 600s (i.e. 10 minutes) and if there was a cheap rate energy + * starting at 00:30, then the EVSE must compute a random delay between 0-599s and add this to its initial planned + * start time. + */ + public Integer randomizationDelayWindow; // 10 elapsed-s RW VM + /** + * Indicates the time, in UTC, when the EVSE plans to start the next scheduled charge based on the charging + * preferences. + * A null value indicates that there is no scheduled charging (for example, the EVSE Mode is set to use Manual mode + * tag), or that the vehicle is not plugged in with the SupplyState indicating that charging is enabled. + */ + public Integer nextChargeStartTime; // 35 epoch-s R V + /** + * Indicates the time, in UTC, when the EVSE SHOULD complete the next scheduled charge based on the charging + * preferences. + * A null value indicates that there is no scheduled charging (for example, the EVSE Mode is set to use Manual mode + * tag), or that the vehicle is not plugged in with the SupplyState indicating that charging is enabled. + */ + public Integer nextChargeTargetTime; // 36 epoch-s R V + /** + * Indicates the amount of energy that the EVSE is going to attempt to add to the vehicle in the next charging + * target. + * A null value indicates that there is no scheduled charging (for example, the EVSE Mode is set to use Manual mode + * tag), or that the vehicle is not plugged in with the SupplyState indicating that charging is enabled, or that the + * next ChargingTargetStruct is using the TargetSoC value to charge the vehicle. + */ + public BigInteger nextChargeRequiredEnergy; // 37 energy-mWh R V + /** + * Indicates the target SoC the EVSE is going to attempt to reach when the vehicle is next charged. + * A null value indicates that there is no scheduled charging (for example, the EVSE Mode is set to use Manual mode + * tag), or that the vehicle is not plugged in with the SupplyState indicating that charging is enabled, or that the + * next ChargingTargetStruct is using the AddedEnergy value to charge the vehicle. + * If the SOC feature is not supported, only the values null and 100% are supported. + */ + public Integer nextChargeTargetSoC; // 38 percent R V + /** + * Indicates the vehicle efficiency rating for a connected vehicle. + * This can be used to help indicate to the user approximately how many miles or km of range will be added. It + * allows user interfaces to display to the user simpler terms that they can relate to compared to kWh. + * This is value is stored in km per kWh multiplied by a scaling factor of 1000. + * A null value indicates that the EV efficiency is unknown and the NextChargeRequiredEnergy attribute cannot be + * converted from Wh to miles or km. + * To convert from Wh into Range: + * AddedRange (km) = AddedEnergy (Wh) x ApproxEVEfficiency (km/kWh x 1000) AddedRange (Miles) = + * AddedEnergy (Wh) x ApproxEVEfficiency (km/kWh x 1000) x 0.6213 + * Example: + * ApproxEVEfficiency (km/kWh x 1000): 4800 (i.e. 4.8km/kWh x 1000) + * ### AddedEnergy (Wh): 10,000 + * AddedRange (km) = 10,000 x 4800 / 1,000,000 = 48 km + * AddedRange (Miles) = AddedEnergy (Wh) x ApproxEVEfficiency (km/kWh x 1000) x + * 0.6213 + * = 29.82 Miles + */ + public Integer approximateEvEfficiency; // 39 uint16 RW VM + /** + * Indicates the state of charge of the EV battery in steps of 1%. The values are in the 0-100%. This attribute is + * only available on EVSEs which can read the state of charge from the vehicle and that support the SOC feature. If + * the StateOfCharge cannot be read from the vehicle it shall be returned with a NULL value. + */ + public Integer stateOfCharge; // 48 percent R V + /** + * Indicates the capacity of the EV battery in mWh. This value is always positive. + */ + public BigInteger batteryCapacity; // 49 energy-mWh R V + /** + * Indicates the vehicle ID read by the EVSE via ISO-15118 using the PNC feature, if the EVSE supports this + * capability. + * The field may be based on the e-Mobility Account Identifier (EMAID). A null value shall indicate that this is + * unknown. + */ + public String vehicleId; // 50 string R V + public Integer sessionId; // 64 uint32 R V + public Integer sessionDuration; // 65 elapsed-s R V + public BigInteger sessionEnergyCharged; // 66 energy-mWh R V + public BigInteger sessionEnergyDischarged; // 67 energy-mWh R V + // Structs + + /** + * This event shall be generated when the EV is plugged in. + */ + public class EvConnected { + /** + * This is the new session ID created after the vehicle is plugged in. + */ + public Integer sessionId; // uint32 + + public EvConnected(Integer sessionId) { + this.sessionId = sessionId; + } + } + + /** + * This event shall be generated when the EV is unplugged or not detected (having been previously plugged in). When + * the vehicle is unplugged then the session is ended. + */ + public class EvNotDetected { + /** + * This field shall indicate the current value of the SessionID attribute. + */ + public Integer sessionId; // uint32 + /** + * This field shall indicate the value of the State attribute prior to the EV not being detected. + */ + public StateEnum state; // StateEnum + /** + * This field shall indicate the total duration of the session, from the start of the session when the EV was + * plugged in, until it was unplugged. + */ + public Integer sessionDuration; // elapsed-s + /** + * This field shall indicate the total amount of energy transferred from the EVSE to the EV during the session. + * Note that if bi-directional charging occurs during the session, then this value shall only include the sum of + * energy transferred from the EVSE to the EV, and shall NOT be a net value of charging and discharging energy. + */ + public BigInteger sessionEnergyCharged; // energy-mWh + /** + * This field shall indicate the total amount of energy transferred from the EV to the EVSE during the session. + * Note that if bi-directional discharging occurs during the session, then this value shall only include the sum + * of energy transferred from the EV to the EVSE, and shall NOT be a net value of charging and discharging + * energy. + */ + public BigInteger sessionEnergyDischarged; // energy-mWh + + public EvNotDetected(Integer sessionId, StateEnum state, Integer sessionDuration, + BigInteger sessionEnergyCharged, BigInteger sessionEnergyDischarged) { + this.sessionId = sessionId; + this.state = state; + this.sessionDuration = sessionDuration; + this.sessionEnergyCharged = sessionEnergyCharged; + this.sessionEnergyDischarged = sessionEnergyDischarged; + } + } + + /** + * This event shall be generated whenever the EV starts charging or discharging, except when an EV has switched + * between charging and discharging under the control of the PowerAdjustment feature of the Device Energy Management + * cluster of the associated Device Energy Management device. + */ + public class EnergyTransferStarted { + /** + * This field shall indicate the value of the SessionID attribute at the time the event was generated. + */ + public Integer sessionId; // uint32 + /** + * This field shall indicate the value of the State attribute at the time the event was generated. + */ + public StateEnum state; // StateEnum + /** + * This field shall indicate the value of the maximum charging current at the time the event was generated. + * A non-zero value indicates that the EV has been enabled for charging and the value is taken directly from the + * MaximumChargeCurrent attribute. A zero value indicates that the EV has not been enabled for charging. + */ + public BigInteger maximumCurrent; // amperage-mA + /** + * This field shall indicate the value of the maximum discharging current at the time the event was generated. + * A non-zero value indicates that the EV has been enabled for discharging and the value is taken directly from + * the MaximumDischargeCurrent attribute. A zero value indicates that the EV has not been enabled for + * discharging. + */ + public BigInteger maximumDischargeCurrent; // amperage-mA + + public EnergyTransferStarted(Integer sessionId, StateEnum state, BigInteger maximumCurrent, + BigInteger maximumDischargeCurrent) { + this.sessionId = sessionId; + this.state = state; + this.maximumCurrent = maximumCurrent; + this.maximumDischargeCurrent = maximumDischargeCurrent; + } + } + + /** + * This event shall be generated whenever the EV stops charging or discharging, except when an EV has switched + * between charging and discharging under the control of the PowerAdjustment feature of the Device Energy Management + * cluster of the associated Device Energy Management device. + */ + public class EnergyTransferStopped { + /** + * This field shall indicate the value of the SessionID attribute prior to the energy transfer stopping. + */ + public Integer sessionId; // uint32 + /** + * This field shall indicate the value of the State attribute prior to the energy transfer stopping. + */ + public StateEnum state; // StateEnum + /** + * This field shall indicate the reason why the energy transferred stopped. + */ + public EnergyTransferStoppedReasonEnum reason; // EnergyTransferStoppedReasonEnum + /** + * This field shall indicate the amount of energy transferred from the EVSE to the EV since the previous + * EnergyTransferStarted event, in mWh. + */ + public BigInteger energyTransferred; // energy-mWh + /** + * This field shall indicate the amount of energy transferred from the EV to the EVSE since the previous + * EnergyTransferStarted event, in mWh. + */ + public BigInteger energyDischarged; // energy-mWh + + public EnergyTransferStopped(Integer sessionId, StateEnum state, EnergyTransferStoppedReasonEnum reason, + BigInteger energyTransferred, BigInteger energyDischarged) { + this.sessionId = sessionId; + this.state = state; + this.reason = reason; + this.energyTransferred = energyTransferred; + this.energyDischarged = energyDischarged; + } + } + + /** + * If the EVSE detects a fault it shall generate a Fault Event. The SupplyState attribute shall be set to + * DisabledError and the type of fault detected by the EVSE shall be stored in the FaultState attribute. + * This event shall be generated when the FaultState changes from any error state. i.e. if it changes from NoError + * to any other state and if the error then clears, this would generate 2 events. + * It is assumed that the fault will be cleared locally on the EVSE device. When all faults have been cleared, the + * EVSE device shall set the FaultState attribute to NoError and the SupplyState attribute shall be set back to its + * previous state. + */ + public class Fault { + /** + * This field shall indicate the value of the SessionID attribute prior to the Fault State being changed. A + * value of null indicates no sessions have occurred before the fault occurred. + */ + public Integer sessionId; // uint32 + /** + * This field shall indicate the value of the State attribute prior to the Fault State being changed. + */ + public StateEnum state; // StateEnum + /** + * This field shall indicate the value of the FaultState attribute prior to the Fault State being changed. + */ + public FaultStateEnum faultStatePreviousState; // FaultStateEnum + /** + * This field shall indicate the current value of the FaultState attribute. + */ + public FaultStateEnum faultStateCurrentState; // FaultStateEnum + + public Fault(Integer sessionId, StateEnum state, FaultStateEnum faultStatePreviousState, + FaultStateEnum faultStateCurrentState) { + this.sessionId = sessionId; + this.state = state; + this.faultStatePreviousState = faultStatePreviousState; + this.faultStateCurrentState = faultStateCurrentState; + } + } + + /** + * This event shall be generated when a RFID card has been read. This allows a controller to register the card ID + * and use this to authenticate and start the charging session. + */ + public class Rfid { + /** + * The UID field (ISO 14443A UID) is either 4, 7 or 10 bytes. + */ + public OctetString uid; // octstr + + public Rfid(OctetString uid) { + this.uid = uid; + } + } + + /** + * This represents a single user specified charging target for an EV. + * An EVSE or EMS system optimizer may use this information to take the Time of Use Tariff, grid carbon intensity, + * local generation (solar PV) into account to provide the cheapest and cleanest energy to the EV. + * The optimization strategy is not defined here, however in simple terms, the AddedEnergy requirement can be + * fulfilled by knowing the charging Power (W) and the time needed to charge. + * To compute the Charging Time: Required Energy (Wh) = Power (W) x ChargingTime (s) / 3600 Therefore: + * ChargingTime (s) = (3600 x RequiredEnergy (wH)) / Power (W) + * To compute the charging time: Charging StartTime = TargetTimeMinutesPastMidnight - ChargingTime + */ + public class ChargingTargetStruct { + /** + * This field shall indicate the desired charging completion time of the associated day. The time will be + * represented by a 16 bits unsigned integer to designate the minutes since midnight. For example, 6am will be + * represented by 360 minutes since midnight and 11:30pm will be represented by 1410 minutes since midnight. + * This field is based on local wall clock time. In case of Daylight Savings Time transition which may result in + * an extra hour or one hour less in the day, the charging algorithm should take into account the shift + * appropriately. + * Note that if the TargetTimeMinutesPastMidnight values are too close together (e.g. 2 per day) these may + * overlap. The EVSE may have to coalesce the charging targets into a single target. e.g. if the 1st charging + * target cannot be met in the time available, the EVSE may be forced to begin working towards the 2nd charging + * target and immediately continue until both targets have been satisfied (or the vehicle becomes full). + * The EVSE itself cannot predict the behavior of the vehicle (i.e. if it cannot obtain the SoC from the + * vehicle), so should attempt to perform a sensible operation based on these targets. It is recommended that + * the charging schedule is pessimistic (i.e. starts earlier) since the vehicle may charge more slowly than the + * electrical supply may provide power (especially if it is cold). + * If the user configures large charging targets (e.g. high values of AddedEnergy or SoC) then it is expected + * that the EVSE may need to begin charging immediately, and may not be able to guarantee that the vehicle will + * be able to reach the target. + */ + public Integer targetTimeMinutesPastMidnight; // uint16 + /** + * This field represents the target SoC that the vehicle should be charged to before the + * TargetTimeMinutesPastMidnight. + * If the EVSE supports the SOC feature and can obtain the SoC of the vehicle: + * • the TargetSoC field shall take precedence over the AddedEnergy field. + * • the EVSE SHOULD charge to the TargetSoC and then stop the charging automatically when it reaches that + * point. + * • if the TargetSoC value is set to 100% then the EVSE SHOULD continue to charge the vehicle until the vehicle + * decides to stop charging. + * If the EVSE does not support the SOC feature or cannot obtain the SoC of the vehicle: + * • the AddedEnergy field shall take precedence over the TargetSoC field, and if the EVSE does not support the + * SOC feature then the TargetSoC field may only take the values null or 100%. + * • if the AddedEnergy field has not been provided, the EVSE SHOULD assume the vehicle is empty and charge + * until the vehicle stops demanding a charge. + */ + public Integer targetSoC; // percent + /** + * This field represents the amount of energy that the user would like to have added to the vehicle before the + * TargetTimeMinutesPastMidnight. + * This represents a positive value in mWh that SHOULD be added during the session (i.e. if the vehicle charging + * is stopped and started several times, this equates to the total energy since the vehicle has been plugged + * in). + * The maximum value (500kWh) is much larger than most EV batteries on the market today. If the client tries to + * set this value too high then the EVSE will need to start charging immediately and continue charging until the + * vehicle stops demanding charge (i.e. it is full). Therefore the maximum value should be set based on typical + * battery size of the vehicles on the market (e.g. 70000Wh), however this is up to the client to carefully + * choose a value. + * > [!NOTE] + * > If the EVSE can obtain the Battery Capacity of the vehicle, it SHOULD NOT limit this AddedEnergy value + * to the Battery Capacity of the vehicle, since the EV may also require energy for heating and cooling of the + * battery during charging, or for heating or cooling the cabin. + */ + public BigInteger addedEnergy; // energy-mWh + + public ChargingTargetStruct(Integer targetTimeMinutesPastMidnight, Integer targetSoC, BigInteger addedEnergy) { + this.targetTimeMinutesPastMidnight = targetTimeMinutesPastMidnight; + this.targetSoC = targetSoC; + this.addedEnergy = addedEnergy; + } + } + + /** + * This represents a set of user specified charging targets for an EV for a set of specified days. + */ + public class ChargingTargetScheduleStruct { + /** + * This field shall indicate the days of the week that the charging targets SHOULD be associated to. This field + * is a bitmap and therefore the associated targets could be applied to multiple days. + */ + public TargetDayOfWeekBitmap dayOfWeekForSequence; // TargetDayOfWeekBitmap + /** + * This field shall indicate a list of up to 10 charging targets for each of the associated days of the week. + */ + public List chargingTargets; // list + + public ChargingTargetScheduleStruct(TargetDayOfWeekBitmap dayOfWeekForSequence, + List chargingTargets) { + this.dayOfWeekForSequence = dayOfWeekForSequence; + this.chargingTargets = chargingTargets; + } + } + + // Enums + public enum StateEnum implements MatterEnum { + NOT_PLUGGED_IN(0, "Not Plugged In"), + PLUGGED_IN_NO_DEMAND(1, "Plugged In No Demand"), + PLUGGED_IN_DEMAND(2, "Plugged In Demand"), + PLUGGED_IN_CHARGING(3, "Plugged In Charging"), + PLUGGED_IN_DISCHARGING(4, "Plugged In Discharging"), + SESSION_ENDING(5, "Session Ending"), + FAULT(6, "Fault"); + + public final Integer value; + public final String label; + + private StateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SupplyStateEnum implements MatterEnum { + DISABLED(0, "Disabled"), + CHARGING_ENABLED(1, "Charging Enabled"), + DISCHARGING_ENABLED(2, "Discharging Enabled"), + DISABLED_ERROR(3, "Disabled Error"), + DISABLED_DIAGNOSTICS(4, "Disabled Diagnostics"), + ENABLED(5, "Enabled"); + + public final Integer value; + public final String label; + + private SupplyStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum FaultStateEnum implements MatterEnum { + NO_ERROR(0, "No Error"), + METER_FAILURE(1, "Meter Failure"), + OVER_VOLTAGE(2, "Over Voltage"), + UNDER_VOLTAGE(3, "Under Voltage"), + OVER_CURRENT(4, "Over Current"), + CONTACT_WET_FAILURE(5, "Contact Wet Failure"), + CONTACT_DRY_FAILURE(6, "Contact Dry Failure"), + GROUND_FAULT(7, "Ground Fault"), + POWER_LOSS(8, "Power Loss"), + POWER_QUALITY(9, "Power Quality"), + PILOT_SHORT_CIRCUIT(10, "Pilot Short Circuit"), + EMERGENCY_STOP(11, "Emergency Stop"), + EV_DISCONNECTED(12, "Ev Disconnected"), + WRONG_POWER_SUPPLY(13, "Wrong Power Supply"), + LIVE_NEUTRAL_SWAP(14, "Live Neutral Swap"), + OVER_TEMPERATURE(15, "Over Temperature"), + OTHER(255, "Other"); + + public final Integer value; + public final String label; + + private FaultStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EnergyTransferStoppedReasonEnum implements MatterEnum { + EV_STOPPED(0, "Ev Stopped"), + EVSE_STOPPED(1, "Evse Stopped"), + OTHER(2, "Other"); + + public final Integer value; + public final String label; + + private EnergyTransferStoppedReasonEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class TargetDayOfWeekBitmap { + public boolean sunday; + public boolean monday; + public boolean tuesday; + public boolean wednesday; + public boolean thursday; + public boolean friday; + public boolean saturday; + + public TargetDayOfWeekBitmap(boolean sunday, boolean monday, boolean tuesday, boolean wednesday, + boolean thursday, boolean friday, boolean saturday) { + this.sunday = sunday; + this.monday = monday; + this.tuesday = tuesday; + this.wednesday = wednesday; + this.thursday = thursday; + this.friday = friday; + this.saturday = saturday; + } + } + + public static class FeatureMap { + /** + * + * Since some EVSEs cannot obtain the SoC from the vehicle, some EV charging solutions allow the consumer to + * specify a daily charging target (for adding energy to the EV’s battery). This feature allows the consumer to + * specify how many miles or km of additional range they need for their typical daily commute. This range + * requirement can be converted into a daily energy demand with a target charging completion time. + * The EVSE itself can use this information (or may allow a controller such as an EMS) to compute an optimized + * charging schedule. + * An EVSE device which includes a Device Energy Management device with the Device Energy Management cluster PFR + * (Power Forecast Reporting) feature can use the charging preferences information to produce its power + * forecast. + * EVSE devices that support the Device Energy Management cluster’s FA feature can have their charging profiles + * set by a controller device such as an EMS. For example, if the EVSE advertises a simple power forecast which + * allows the EMS to adjust over a wide range of power and time durations, then the EVSE may allow the EMS to + * propose a revised optimized forecast (which is the charging profile). For example, a solar PV ESA may also + * share its Forecast, so enabling an EMS to adjust the EVSE forecast to the best time to charge so that any + * excess solar generation is used to charge the EV. + * See the Device Energy Management Cluster for more details. + */ + public boolean chargingPreferences; + /** + * + * Vehicles and EVSEs which support ISO 15118 may allow the vehicle to report its battery size and state of + * charge. If the EVSE supports PLC it may have a vehicle connected which optionally supports reporting of its + * battery size and current State of Charge (SoC). + * If the EVSE supports reporting of State of Charge this feature will only work if a compatible EV is + * connected. + * Note some EVSEs may use other undefined mechanisms to obtain vehicle State of Charge outside the scope of + * this cluster. + */ + public boolean soCReporting; + /** + * + * If the EVSE supports PLC, it may be able to support the Plug and Charge feature. e.g. this may allow the + * vehicle ID to be obtained which may allow an energy management system to track energy usage per vehicle (e.g. + * to give the owner an indicative cost of charging, or for work place charging). + * If the EVSE supports the Plug and Charge feature, it will only work if a compatible EV is connected. + */ + public boolean plugAndCharge; + /** + * + * If the EVSE is fitted with an RFID reader, it may be possible to obtain the User or Vehicle ID from an RFID + * card. This may be used to record a charging session against a specific charging account, and may optionally + * be used to authorize a charging session. + * An RFID event can be generated when a user taps an RFID card onto the RFID reader. The event must be + * subscribed to by the EVSE Management cluster client. This client may use this to enable the EV to charge or + * discharge. The lookup and authorization of RIFD UID is outside the scope of this cluster. + */ + public boolean rfid; + /** + * + * If the EVSE can support bi-directional charging, it may be possible to request that the vehicle can discharge + * to the home or grid. + * The charging and discharging may be controlled by a home Energy Management System (EMS) using the Device + * Energy Management cluster of the associated Device Energy Management device. For example, an EMS may use the + * PA (Power Adjustment) feature to continually adjust the charge/discharge current to/from the EV so as to + * minimise the energy flow from/to the grid as the demand in the home and the solar supply to the home both + * fluctuate. + */ + public boolean v2X; + + public FeatureMap(boolean chargingPreferences, boolean soCReporting, boolean plugAndCharge, boolean rfid, + boolean v2X) { + this.chargingPreferences = chargingPreferences; + this.soCReporting = soCReporting; + this.plugAndCharge = plugAndCharge; + this.rfid = rfid; + this.v2X = v2X; + } + } + + public EnergyEvseCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 153, "EnergyEvse"); + } + + protected EnergyEvseCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Allows a client to disable the EVSE from charging and discharging. + */ + public static ClusterCommand disable() { + return new ClusterCommand("disable"); + } + + /** + * This command allows a client to enable the EVSE to charge an EV, and to provide or update the maximum and minimum + * charge current. + */ + public static ClusterCommand enableCharging(Integer chargingEnabledUntil, BigInteger minimumChargeCurrent, + BigInteger maximumChargeCurrent) { + Map map = new LinkedHashMap<>(); + if (chargingEnabledUntil != null) { + map.put("chargingEnabledUntil", chargingEnabledUntil); + } + if (minimumChargeCurrent != null) { + map.put("minimumChargeCurrent", minimumChargeCurrent); + } + if (maximumChargeCurrent != null) { + map.put("maximumChargeCurrent", maximumChargeCurrent); + } + return new ClusterCommand("enableCharging", map); + } + + /** + * Upon receipt, this shall allow a client to enable the discharge of an EV, and to provide or update the maximum + * discharge current. + */ + public static ClusterCommand enableDischarging(Integer dischargingEnabledUntil, + BigInteger maximumDischargeCurrent) { + Map map = new LinkedHashMap<>(); + if (dischargingEnabledUntil != null) { + map.put("dischargingEnabledUntil", dischargingEnabledUntil); + } + if (maximumDischargeCurrent != null) { + map.put("maximumDischargeCurrent", maximumDischargeCurrent); + } + return new ClusterCommand("enableDischarging", map); + } + + /** + * Allows a client to put the EVSE into a self-diagnostics mode. + */ + public static ClusterCommand startDiagnostics() { + return new ClusterCommand("startDiagnostics"); + } + + /** + * Allows a client to set the user specified charging targets. + */ + public static ClusterCommand setTargets(List chargingTargetSchedules) { + Map map = new LinkedHashMap<>(); + if (chargingTargetSchedules != null) { + map.put("chargingTargetSchedules", chargingTargetSchedules); + } + return new ClusterCommand("setTargets", map); + } + + /** + * Allows a client to retrieve the current set of charging targets. + */ + public static ClusterCommand getTargets() { + return new ClusterCommand("getTargets"); + } + + /** + * Allows a client to clear all stored charging targets. + */ + public static ClusterCommand clearTargets() { + return new ClusterCommand("clearTargets"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "state : " + state + "\n"; + str += "supplyState : " + supplyState + "\n"; + str += "faultState : " + faultState + "\n"; + str += "chargingEnabledUntil : " + chargingEnabledUntil + "\n"; + str += "dischargingEnabledUntil : " + dischargingEnabledUntil + "\n"; + str += "circuitCapacity : " + circuitCapacity + "\n"; + str += "minimumChargeCurrent : " + minimumChargeCurrent + "\n"; + str += "maximumChargeCurrent : " + maximumChargeCurrent + "\n"; + str += "maximumDischargeCurrent : " + maximumDischargeCurrent + "\n"; + str += "userMaximumChargeCurrent : " + userMaximumChargeCurrent + "\n"; + str += "randomizationDelayWindow : " + randomizationDelayWindow + "\n"; + str += "nextChargeStartTime : " + nextChargeStartTime + "\n"; + str += "nextChargeTargetTime : " + nextChargeTargetTime + "\n"; + str += "nextChargeRequiredEnergy : " + nextChargeRequiredEnergy + "\n"; + str += "nextChargeTargetSoC : " + nextChargeTargetSoC + "\n"; + str += "approximateEvEfficiency : " + approximateEvEfficiency + "\n"; + str += "stateOfCharge : " + stateOfCharge + "\n"; + str += "batteryCapacity : " + batteryCapacity + "\n"; + str += "vehicleId : " + vehicleId + "\n"; + str += "sessionId : " + sessionId + "\n"; + str += "sessionDuration : " + sessionDuration + "\n"; + str += "sessionEnergyCharged : " + sessionEnergyCharged + "\n"; + str += "sessionEnergyDischarged : " + sessionEnergyDischarged + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyEvseModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyEvseModeCluster.java new file mode 100644 index 00000000000..886fcf121f1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyEvseModeCluster.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * EnergyEvseMode + * + * @author Dan Cunningham - Initial contribution + */ +public class EnergyEvseModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x009D; + public static final String CLUSTER_NAME = "EnergyEvseMode"; + public static final String CLUSTER_PREFIX = "energyEvseMode"; + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + MANUAL(16384, "Manual"), + TIME_OF_USE(16385, "Time Of Use"), + SOLAR_CHARGING(16386, "Solar Charging"), + V2X(16387, "V 2 X"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public EnergyEvseModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 157, "EnergyEvseMode"); + } + + protected EnergyEvseModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyPreferenceCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyPreferenceCluster.java new file mode 100644 index 00000000000..35d7735da81 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EnergyPreferenceCluster.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * EnergyPreference + * + * @author Dan Cunningham - Initial contribution + */ +public class EnergyPreferenceCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x009B; + public static final String CLUSTER_NAME = "EnergyPreference"; + public static final String CLUSTER_PREFIX = "energyPreference"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ENERGY_BALANCES = "energyBalances"; + public static final String ATTRIBUTE_CURRENT_ENERGY_BALANCE = "currentEnergyBalance"; + public static final String ATTRIBUTE_ENERGY_PRIORITIES = "energyPriorities"; + public static final String ATTRIBUTE_LOW_POWER_MODE_SENSITIVITIES = "lowPowerModeSensitivities"; + public static final String ATTRIBUTE_CURRENT_LOW_POWER_MODE_SENSITIVITY = "currentLowPowerModeSensitivity"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates a list of BalanceStructs, each representing a step along a linear scale of relative priorities. A Step + * field with a value of zero shall indicate that the device SHOULD entirely favor the priority specified by the + * first element in EnergyPriorities; whereas a Step field with a value of 100 shall indicate that the device SHOULD + * entirely favor the priority specified by the second element in EnergyPriorities. The midpoint value of 50 shall + * indicate an even split between the two priorities. + * This shall contain at least two BalanceStructs. + * Each BalanceStruct shall have a Step field larger than the Step field on the previous BalanceStruct in the list. + * The first BalanceStruct shall have a Step value of zero, and the last BalanceStruct shall have a Step value of + * 100. + */ + public List energyBalances; // 0 list R V + /** + * Indicates the current preference of the user for balancing different priorities during device use. The value of + * this attribute is the index, 0-based, into the EnergyBalances attribute for the currently selected balance. + * If an attempt is made to set this attribute to an index outside the maximum index for EnergyBalances, a response + * with the status code CONSTRAINT_ERROR shall be returned. + * If the value of EnergyBalances changes after an update, the device shall migrate the value of the + * CurrentEnergyBalance attribute to the index which the manufacturer specifies most closely matches the previous + * value, while preserving extreme preferences as follows: + * 1. If the previous value of CurrentEnergyBalance was zero, indicating a total preference for the priority + * specified by the first element in EnergyPriorities, the new value of CurrentEnergyBalance shall also be zero. + * 2. If the previous value of CurrentEnergyBalance was the index of the last BalanceStruct in the previous value of + * EnergyBalances, indicating a total preference for the priority specified by the last element in EnergyPriorities, + * the new value of CurrentEnergyBalance shall be the index of the last element in the updated value of + * EnergyBalances. + */ + public Integer currentEnergyBalance; // 1 uint8 RW VO + /** + * Indicates two extremes for interpreting the values in the EnergyBalances attribute. These two priorities shall be + * in opposition to each other; e.g. Comfort vs. Efficiency or Speed vs. WaterConsumption. + * If the value of EnergyPriorities changes after an update to represent a new balance between priorities, the value + * of the CurrentEnergyBalance attribute shall be set to its default. + */ + public List energyPriorities; // 2 list R V + /** + * Indicates a list of BalanceStructs, each representing a condition or set of conditions for the device to enter a + * low power mode. + * This shall contain at least two BalanceStructs. + * Each BalanceStruct shall have a Step field larger than the Step field on the previous BalanceStruct in the list. + */ + public List lowPowerModeSensitivities; // 3 list R V + /** + * Indicates the current preference of the user for determining when the device should enter a low power mode. The + * value of this attribute is the index, 0-based, into the LowPowerModeSensitivities attribute for the currently + * selected preference. + * If an attempt is made to set this attribute to an index outside the maximum index for LowPowerModeSensitivities, + * a response with the status code CONSTRAINT_ERROR shall be returned. + * If the value of LowPowerModeSensitivities changes after an update, the device shall migrate the value of the + * LowPowerModeSensitivity attribute to the index which the manufacturer specifies most closely matches the previous + * value. + */ + public Integer currentLowPowerModeSensitivity; // 4 uint8 RW VO + // Structs + + /** + * This represents a step along a scale of preferences. + */ + public class BalanceStruct { + /** + * This field shall indicate the relative value of this step. + */ + public Integer step; // percent + /** + * This field shall indicate an optional string explaining which actions a device might take at the given step + * value. + */ + public String label; // string + + public BalanceStruct(Integer step, String label) { + this.step = step; + this.label = label; + } + } + + // Enums + public enum EnergyPriorityEnum implements MatterEnum { + COMFORT(0, "Comfort"), + SPEED(1, "Speed"), + EFFICIENCY(2, "Efficiency"), + WATER_CONSUMPTION(3, "Water Consumption"); + + public final Integer value; + public final String label; + + private EnergyPriorityEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature allows a user to select from a list of energy balances with associated descriptions of which + * strategies a device will use to target the specified balance. + */ + public boolean energyBalance; + /** + * + * This feature allows the user to select a condition or set of conditions which will cause the device to switch + * to a mode using less power. For example, a device might provide a scale of durations that must elapse without + * user interaction before it goes to sleep. + */ + public boolean lowPowerModeSensitivity; + + public FeatureMap(boolean energyBalance, boolean lowPowerModeSensitivity) { + this.energyBalance = energyBalance; + this.lowPowerModeSensitivity = lowPowerModeSensitivity; + } + } + + public EnergyPreferenceCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 155, "EnergyPreference"); + } + + protected EnergyPreferenceCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "energyBalances : " + energyBalances + "\n"; + str += "currentEnergyBalance : " + currentEnergyBalance + "\n"; + str += "energyPriorities : " + energyPriorities + "\n"; + str += "lowPowerModeSensitivities : " + lowPowerModeSensitivities + "\n"; + str += "currentLowPowerModeSensitivity : " + currentLowPowerModeSensitivity + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EthernetNetworkDiagnosticsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EthernetNetworkDiagnosticsCluster.java new file mode 100644 index 00000000000..c327ad0fb6e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/EthernetNetworkDiagnosticsCluster.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * EthernetNetworkDiagnostics + * + * @author Dan Cunningham - Initial contribution + */ +public class EthernetNetworkDiagnosticsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0037; + public static final String CLUSTER_NAME = "EthernetNetworkDiagnostics"; + public static final String CLUSTER_PREFIX = "ethernetNetworkDiagnostics"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_PHY_RATE = "phyRate"; + public static final String ATTRIBUTE_FULL_DUPLEX = "fullDuplex"; + public static final String ATTRIBUTE_PACKET_RX_COUNT = "packetRxCount"; + public static final String ATTRIBUTE_PACKET_TX_COUNT = "packetTxCount"; + public static final String ATTRIBUTE_TX_ERR_COUNT = "txErrCount"; + public static final String ATTRIBUTE_COLLISION_COUNT = "collisionCount"; + public static final String ATTRIBUTE_OVERRUN_COUNT = "overrunCount"; + public static final String ATTRIBUTE_CARRIER_DETECT = "carrierDetect"; + public static final String ATTRIBUTE_TIME_SINCE_RESET = "timeSinceReset"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The PHYRate attribute shall indicate the current nominal, usable speed at the top of the physical layer of the + * Node. A value of null shall indicate that the interface is not currently configured or operational. + */ + public PHYRateEnum phyRate; // 0 PHYRateEnum R V + /** + * The FullDuplex attribute shall indicate if the Node is currently utilizing the full-duplex operating mode. A + * value of null shall indicate that the interface is not currently configured or operational. + */ + public Boolean fullDuplex; // 1 bool R V + /** + * The PacketRxCount attribute shall indicate the number of packets that have been received on the ethernet network + * interface. The PacketRxCount attribute shall be reset to 0 upon a reboot of the Node. + */ + public BigInteger packetRxCount; // 2 uint64 R V + /** + * The PacketTxCount attribute shall indicate the number of packets that have been successfully transferred on the + * ethernet network interface. The PacketTxCount attribute shall be reset to 0 upon a reboot of the Node. + */ + public BigInteger packetTxCount; // 3 uint64 R V + /** + * The TxErrCount attribute shall indicate the number of failed packet transmissions that have occurred on the + * ethernet network interface. The TxErrCount attribute shall be reset to 0 upon a reboot of the Node. + */ + public BigInteger txErrCount; // 4 uint64 R V + /** + * The CollisionCount attribute shall indicate the number of collisions that have occurred while attempting to + * transmit a packet on the ethernet network interface. The CollisionCount attribute shall be reset to 0 upon a + * reboot of the Node. + */ + public BigInteger collisionCount; // 5 uint64 R V + /** + * The OverrunCount attribute shall indicate the number of packets dropped either at ingress or egress, due to lack + * of buffer memory to retain all packets on the ethernet network interface. The OverrunCount attribute shall be + * reset to 0 upon a reboot of the Node. + */ + public BigInteger overrunCount; // 6 uint64 R V + /** + * The CarrierDetect attribute shall indicate the value of the Carrier Detect control signal present on the ethernet + * network interface. A value of null shall indicate that the interface is not currently configured or operational. + */ + public Boolean carrierDetect; // 7 bool R V + /** + * The TimeSinceReset attribute shall indicate the duration of time, in minutes, that it has been since the ethernet + * network interface has reset for any reason. + */ + public BigInteger timeSinceReset; // 8 uint64 R V + + // Enums + public enum PHYRateEnum implements MatterEnum { + RATE10M(0, "Rate 10 M"), + RATE100M(1, "Rate 100 M"), + RATE1G(2, "Rate 1 G"), + RATE25G(3, "Rate 25 G"), + RATE5G(4, "Rate 5 G"), + RATE10G(5, "Rate 10 G"), + RATE40G(6, "Rate 40 G"), + RATE100G(7, "Rate 100 G"), + RATE200G(8, "Rate 200 G"), + RATE400G(9, "Rate 400 G"); + + public final Integer value; + public final String label; + + private PHYRateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Node makes available the counts for the number of received and transmitted packets on the ethernet interface. + */ + public boolean packetCounts; + /** + * + * Node makes available the counts for the number of errors that have occurred during the reception and + * transmission of packets on the ethernet interface. + */ + public boolean errorCounts; + + public FeatureMap(boolean packetCounts, boolean errorCounts) { + this.packetCounts = packetCounts; + this.errorCounts = errorCounts; + } + } + + public EthernetNetworkDiagnosticsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 55, "EthernetNetworkDiagnostics"); + } + + protected EthernetNetworkDiagnosticsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Reception of this command shall reset the following attributes to 0: + * • PacketRxCount + * • PacketTxCount + * • TxErrCount + * • CollisionCount + * • OverrunCount + * This command has no associated data. + */ + public static ClusterCommand resetCounts() { + return new ClusterCommand("resetCounts"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "phyRate : " + phyRate + "\n"; + str += "fullDuplex : " + fullDuplex + "\n"; + str += "packetRxCount : " + packetRxCount + "\n"; + str += "packetTxCount : " + packetTxCount + "\n"; + str += "txErrCount : " + txErrCount + "\n"; + str += "collisionCount : " + collisionCount + "\n"; + str += "overrunCount : " + overrunCount + "\n"; + str += "carrierDetect : " + carrierDetect + "\n"; + str += "timeSinceReset : " + timeSinceReset + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FanControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FanControlCluster.java new file mode 100644 index 00000000000..138eea7a51c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FanControlCluster.java @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * FanControl + * + * @author Dan Cunningham - Initial contribution + */ +public class FanControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0202; + public static final String CLUSTER_NAME = "FanControl"; + public static final String CLUSTER_PREFIX = "fanControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_FAN_MODE = "fanMode"; + public static final String ATTRIBUTE_FAN_MODE_SEQUENCE = "fanModeSequence"; + public static final String ATTRIBUTE_PERCENT_SETTING = "percentSetting"; + public static final String ATTRIBUTE_PERCENT_CURRENT = "percentCurrent"; + public static final String ATTRIBUTE_SPEED_MAX = "speedMax"; + public static final String ATTRIBUTE_SPEED_SETTING = "speedSetting"; + public static final String ATTRIBUTE_SPEED_CURRENT = "speedCurrent"; + public static final String ATTRIBUTE_ROCK_SUPPORT = "rockSupport"; + public static final String ATTRIBUTE_ROCK_SETTING = "rockSetting"; + public static final String ATTRIBUTE_WIND_SUPPORT = "windSupport"; + public static final String ATTRIBUTE_WIND_SETTING = "windSetting"; + public static final String ATTRIBUTE_AIRFLOW_DIRECTION = "airflowDirection"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the current speed mode of the fan. This attribute may be written by the client to request a different + * fan mode. A server shall return INVALID_IN_STATE to indicate that the fan is not in a state where the FanMode can + * be changed to the requested value. A server may have FanMode values that it can never be set to. For example, + * where this cluster appears on the same or another endpoint as other clusters with a system dependency, for + * example the Thermostat cluster, attempting to set the FanMode attribute of this cluster to Off may not be allowed + * by the system. + * This attribute shall be set to one of the values in FanModeEnum. + * When the FanMode attribute is successfully written to, the PercentSetting and SpeedSetting (if present) + * attributes shall be set to appropriate values, as defined by the Section 4.4.6.3.1 and Section 4.4.6.6.1 + * respectively, unless otherwise specified below. + * When the FanMode attribute is set to any given mode, the PercentCurrent and SpeedCurrent (if present) shall + * indicate the actual currently operating fan speed, unless otherwise specified below. + */ + public FanModeEnum fanMode; // 0 FanModeEnum RW VO + /** + * This attribute indicates the fan speed ranges that shall be supported. + */ + public FanModeSequenceEnum fanModeSequence; // 1 FanModeSequenceEnum R V + /** + * Indicates the speed setting for the fan. This attribute may be written by the client to indicate a new fan speed. + * If the client writes null to this attribute, the attribute value shall NOT change. A server shall return + * INVALID_IN_STATE to indicate that the fan is not in a state where the PercentSetting can be changed to the + * requested value. + * If this is successfully written to 0, the server shall set the FanMode attribute value to Off. + */ + public Integer percentSetting; // 2 percent RW VO + /** + * Indicates the actual currently operating fan speed, or zero to indicate that the fan is off. There may be a + * temporary mismatch between the value of this attribute and the value of the PercentSetting attribute due to other + * system requirements that would not allow the fan to operate at the requested setting. See Section 4.4.6.3.1 for + * more details. + */ + public Integer percentCurrent; // 3 percent R V + /** + * Indicates that the fan has one speed (value of 1) or the maximum speed, if the fan is capable of multiple speeds. + */ + public Integer speedMax; // 4 uint8 R V + /** + * Indicates the speed setting for the fan. This attribute may be written by the client to indicate a new fan speed. + * If the client writes null to this attribute, the attribute value shall NOT change. A server shall return + * INVALID_IN_STATE to indicate that the fan is not in a state where the SpeedSetting can be changed to the + * requested value. + * If this is successfully written to 0, the server shall set the FanMode attribute value to Off. Please see the + * Section 4.4.6.6.1 for details on other values. + */ + public Integer speedSetting; // 5 uint8 RW VO + /** + * Indicates the actual currently operating fan speed, or zero to indicate that the fan is off. There may be a + * temporary mismatch between the value of this attribute and the value of the SpeedSetting attribute due to other + * system requirements that would not allow the fan to operate at the requested setting. + */ + public Integer speedCurrent; // 6 uint8 R V + /** + * This attribute is a bitmap that indicates what rocking motions the server supports. + */ + public RockBitmap rockSupport; // 7 RockBitmap R V + /** + * This attribute is a bitmap that indicates the current active fan rocking motion settings. Each bit shall only be + * set to 1, if the corresponding bit in the RockSupport attribute is set to 1, otherwise a status code of + * CONSTRAINT_ERROR shall be returned. + * If a combination of supported bits is set by the client, and the server does not support the combination, the + * lowest supported single bit in the combination shall be set and active, and all other bits shall indicate zero. + * For example: If RockUpDown and RockRound are both set, but this combination is not possible, then only RockUpDown + * becomes active. + */ + public RockBitmap rockSetting; // 8 RockBitmap RW VO + /** + * This attribute is a bitmap that indicates what wind modes the server supports. At least one wind mode bit shall + * be set. + */ + public WindBitmap windSupport; // 9 WindBitmap R V + /** + * This attribute is a bitmap that indicates the current active fan wind feature settings. Each bit shall only be + * set to 1, if the corresponding bit in the WindSupport attribute is set to 1, otherwise a status code of + * CONSTRAINT_ERROR shall be returned. + * If a combination of supported bits is set by the client, and the server does not support the combination, the + * lowest supported single bit in the combination shall be set and active, and all other bits shall indicate zero. + * For example: If Sleep Wind and Natural Wind are set, but this combination is not possible, then only Sleep Wind + * becomes active. + */ + public WindBitmap windSetting; // 10 WindBitmap RW VO + /** + * Indicates the current airflow direction of the fan. This attribute may be written by the client to indicate a new + * airflow direction for the fan. This attribute shall be set to one of the values in the AirflowDirectionEnum + * table. + */ + public AirflowDirectionEnum airflowDirection; // 11 AirflowDirectionEnum RW VO + + // Enums + public enum StepDirectionEnum implements MatterEnum { + INCREASE(0, "Increase"), + DECREASE(1, "Decrease"); + + public final Integer value; + public final String label; + + private StepDirectionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum AirflowDirectionEnum implements MatterEnum { + FORWARD(0, "Forward"), + REVERSE(1, "Reverse"); + + public final Integer value; + public final String label; + + private AirflowDirectionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum FanModeEnum implements MatterEnum { + OFF(0, "Off"), + LOW(1, "Low"), + MEDIUM(2, "Medium"), + HIGH(3, "High"), + ON(4, "On"), + AUTO(5, "Auto"), + SMART(6, "Smart"); + + public final Integer value; + public final String label; + + private FanModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum FanModeSequenceEnum implements MatterEnum { + OFF_LOW_MED_HIGH(0, "Off Low Med High"), + OFF_LOW_HIGH(1, "Off Low High"), + OFF_LOW_MED_HIGH_AUTO(2, "Off Low Med High Auto"), + OFF_LOW_HIGH_AUTO(3, "Off Low High Auto"), + OFF_HIGH_AUTO(4, "Off High Auto"), + OFF_HIGH(5, "Off High"); + + public final Integer value; + public final String label; + + private FanModeSequenceEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class RockBitmap { + public boolean rockLeftRight; + public boolean rockUpDown; + public boolean rockRound; + + public RockBitmap(boolean rockLeftRight, boolean rockUpDown, boolean rockRound) { + this.rockLeftRight = rockLeftRight; + this.rockUpDown = rockUpDown; + this.rockRound = rockRound; + } + } + + public static class WindBitmap { + public boolean sleepWind; + public boolean naturalWind; + + public WindBitmap(boolean sleepWind, boolean naturalWind) { + this.sleepWind = sleepWind; + this.naturalWind = naturalWind; + } + } + + public static class FeatureMap { + /** + * + * Legacy Fan Control cluster revision 0-1 defined 3 speeds (low, medium and high) plus automatic speed control + * but left it up to the implementer to decide what was supported. Therefore, it is assumed that legacy client + * implementations are capable of determining, from the server, the number of speeds supported between 1, 2, or + * 3, and whether automatic speed control is supported. + * The MultiSpeed feature includes new attributes that support a running fan speed value from 0 to SpeedMax, + * which has a maximum of 100. + * See Section 4.4.6.6.1 for more details. + */ + public boolean multiSpeed; + /** + * + * Automatic mode supported for fan speed + */ + public boolean auto; + /** + * + * Rocking movement supported + */ + public boolean rocking; + /** + * + * Wind emulation supported + */ + public boolean wind; + /** + * + * Step command supported + */ + public boolean step; + /** + * + * Airflow Direction attribute is supported + */ + public boolean airflowDirection; + + public FeatureMap(boolean multiSpeed, boolean auto, boolean rocking, boolean wind, boolean step, + boolean airflowDirection) { + this.multiSpeed = multiSpeed; + this.auto = auto; + this.rocking = rocking; + this.wind = wind; + this.step = step; + this.airflowDirection = airflowDirection; + } + } + + public FanControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 514, "FanControl"); + } + + protected FanControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command speeds up or slows down the fan, in steps, without the client having to know the fan speed. This + * command supports, for example, a user operated wall switch, where the user provides the feedback or control to + * stop sending this command when the proper speed is reached. The step speed values are implementation specific. + * How many step speeds are implemented is implementation specific. + * This command supports these fields: + */ + public static ClusterCommand step(StepDirectionEnum direction, Boolean wrap, Boolean lowestOff) { + Map map = new LinkedHashMap<>(); + if (direction != null) { + map.put("direction", direction); + } + if (wrap != null) { + map.put("wrap", wrap); + } + if (lowestOff != null) { + map.put("lowestOff", lowestOff); + } + return new ClusterCommand("step", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "fanMode : " + fanMode + "\n"; + str += "fanModeSequence : " + fanModeSequence + "\n"; + str += "percentSetting : " + percentSetting + "\n"; + str += "percentCurrent : " + percentCurrent + "\n"; + str += "speedMax : " + speedMax + "\n"; + str += "speedSetting : " + speedSetting + "\n"; + str += "speedCurrent : " + speedCurrent + "\n"; + str += "rockSupport : " + rockSupport + "\n"; + str += "rockSetting : " + rockSetting + "\n"; + str += "windSupport : " + windSupport + "\n"; + str += "windSetting : " + windSetting + "\n"; + str += "airflowDirection : " + airflowDirection + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FixedLabelCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FixedLabelCluster.java new file mode 100644 index 00000000000..8b1b93eb4c7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FixedLabelCluster.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * FixedLabel + * + * @author Dan Cunningham - Initial contribution + */ +public class FixedLabelCluster extends LabelCluster { + + public static final int CLUSTER_ID = 0x0040; + public static final String CLUSTER_NAME = "FixedLabel"; + public static final String CLUSTER_PREFIX = "fixedLabel"; + + public FixedLabelCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 64, "FixedLabel"); + } + + protected FixedLabelCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FlowMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FlowMeasurementCluster.java new file mode 100644 index 00000000000..02a67ff38cf --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FlowMeasurementCluster.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * FlowMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class FlowMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0404; + public static final String CLUSTER_NAME = "FlowMeasurement"; + public static final String CLUSTER_PREFIX = "flowMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_MEASURED_VALUE = "measuredValue"; + public static final String ATTRIBUTE_MIN_MEASURED_VALUE = "minMeasuredValue"; + public static final String ATTRIBUTE_MAX_MEASURED_VALUE = "maxMeasuredValue"; + public static final String ATTRIBUTE_TOLERANCE = "tolerance"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the flow in m/h as follows: + * MeasuredValue = 10 x Flow + * The null value indicates that the flow measurement is unknown, otherwise the range shall be as described in + * Measured Value. + */ + public Integer measuredValue; // 0 uint16 R V + /** + * Indicates the minimum value of MeasuredValue that can be measured. See Measured Value for more details. + * The null value indicates that the value is not available. + */ + public Integer minMeasuredValue; // 1 uint16 R V + /** + * Indicates the maximum value of MeasuredValue that can be measured. See Measured Value for more details. + * The null value indicates that the value is not available. + */ + public Integer maxMeasuredValue; // 2 uint16 R V + /** + * See Measured Value. + */ + public Integer tolerance; // 3 uint16 R V + + public FlowMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1028, "FlowMeasurement"); + } + + protected FlowMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "measuredValue : " + measuredValue + "\n"; + str += "minMeasuredValue : " + minMeasuredValue + "\n"; + str += "maxMeasuredValue : " + maxMeasuredValue + "\n"; + str += "tolerance : " + tolerance + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FormaldehydeConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FormaldehydeConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..88953e60896 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/FormaldehydeConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * FormaldehydeConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class FormaldehydeConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x042B; + public static final String CLUSTER_NAME = "FormaldehydeConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "formaldehydeConcentrationMeasurement"; + + public FormaldehydeConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1067, "FormaldehydeConcentrationMeasurement"); + } + + protected FormaldehydeConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GeneralCommissioningCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GeneralCommissioningCluster.java new file mode 100644 index 00000000000..294662af090 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GeneralCommissioningCluster.java @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * GeneralCommissioning + * + * @author Dan Cunningham - Initial contribution + */ +public class GeneralCommissioningCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0030; + public static final String CLUSTER_NAME = "GeneralCommissioning"; + public static final String CLUSTER_PREFIX = "generalCommissioning"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_BREADCRUMB = "breadcrumb"; + public static final String ATTRIBUTE_BASIC_COMMISSIONING_INFO = "basicCommissioningInfo"; + public static final String ATTRIBUTE_REGULATORY_CONFIG = "regulatoryConfig"; + public static final String ATTRIBUTE_LOCATION_CAPABILITY = "locationCapability"; + public static final String ATTRIBUTE_SUPPORTS_CONCURRENT_CONNECTION = "supportsConcurrentConnection"; + public static final String ATTRIBUTE_TC_ACCEPTED_VERSION = "tcAcceptedVersion"; + public static final String ATTRIBUTE_TC_MIN_REQUIRED_VERSION = "tcMinRequiredVersion"; + public static final String ATTRIBUTE_TC_ACKNOWLEDGEMENTS = "tcAcknowledgements"; + public static final String ATTRIBUTE_TC_ACKNOWLEDGEMENTS_REQUIRED = "tcAcknowledgementsRequired"; + public static final String ATTRIBUTE_TC_UPDATE_DEADLINE = "tcUpdateDeadline"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute allows for the storage of a client-provided small payload which Administrators and Commissioners + * may write and then subsequently read, to keep track of their own progress. This may be used by the Commissioner + * to avoid repeating already-executed actions upon re-establishing a commissioning link after an error. + * On start/restart of the server, such as when a device is power-cycled, this attribute shall be reset to zero. + * Some commands related to commissioning also have a side-effect of updating or resetting this attribute and this + * is specified in their respective functional descriptions. + * The format of the value within this attribute is unspecified and its value is not otherwise used by the + * functioning of any cluster, other than being set as a side-effect of commands where this behavior is described. + */ + public BigInteger breadcrumb; // 0 uint64 RW VA + /** + * This attribute shall describe critical parameters needed at the beginning of commissioning flow. See + * BasicCommissioningInfo for more information. + */ + public BasicCommissioningInfo basicCommissioningInfo; // 1 BasicCommissioningInfo R V + /** + * Indicates the regulatory configuration for the product. + * Note that the country code is part of Basic Information Cluster and therefore NOT listed on the RegulatoryConfig + * attribute. + */ + public RegulatoryLocationTypeEnum regulatoryConfig; // 2 RegulatoryLocationTypeEnum R V + /** + * LocationCapability is statically set by the manufacturer and indicates if this Node needs to be told an exact + * RegulatoryLocation. For example a Node which is "Indoor Only" would not be certified for outdoor use at + * all, and thus there is no need for a commissioner to set or ask the user about whether the device will be used + * inside or outside. However a device which states its capability is "Indoor/Outdoor" means it would like + * clarification if possible. + * For Nodes without radio network interfaces (e.g. Ethernet-only devices), the value IndoorOutdoor shall always be + * used. + * The default value of the RegulatoryConfig attribute is the value of LocationCapability attribute. This means + * devices always have a safe default value, and Commissioners which choose to implement smarter handling can. + */ + public RegulatoryLocationTypeEnum locationCapability; // 3 RegulatoryLocationTypeEnum R V + /** + * This attribute shall indicate whether this device supports "concurrent connection flow" commissioning + * mode (see Section 5.5, “Commissioning Flows”). If false, the device only supports "non-concurrent connection + * flow" mode. + */ + public Boolean supportsConcurrentConnection; // 4 bool R V + /** + * Indicates the last version of the T&Cs for which the device received user acknowledgements. On factory reset + * this field shall be reset to 0. + * When Custom Commissioning Flow is used to obtain user consent (e. g. because the Commissioner does not support + * the TC feature), the manufacturer-provided means for obtaining user consent shall ensure that this attribute is + * set to a value which is greater than or equal to TCMinRequiredVersion before returning the user back to the + * originating Commissioner (see Enhanced Setup Flow). + */ + public Integer tcAcceptedVersion; // 5 uint16 R A + /** + * Indicates the minimum version of the texts presented by the Enhanced Setup Flow that need to be accepted by the + * user for this device. This attribute may change as the result of an OTA update. + * If an event such as a software update causes TCAcceptedVersion to become less than TCMinRequiredVersion, then the + * device shall update TCAcknowledgementsRequired to True so that an administrator can detect that a newer version + * of the texts needs to be presented to the user. + */ + public Integer tcMinRequiredVersion; // 6 uint16 R A + /** + * Indicates the user’s response to the presented terms. Each bit position corresponds to a user response for the + * associated index of matching text, such that bit 0 (bit value 1) is for text index 0. Bit 15 (bit value 0x8000) + * is for text index 15. A bit value of 1 indicates acceptance and a value of 0 indicates non-acceptance. For + * example, if there are two texts that were presented where the first (bit 0, value 1) was declined and the second + * accepted (bit 1, value 2), we would expect the resulting value of the map to be 2. + * Whenever a user provides responses to newly presented terms and conditions, this attribute shall be updated with + * the latest responses. This may happen in response to updated terms that were presented to the user. On a factory + * reset this field shall be reset with all bits set to 0. + */ + public TcAcknowledgements tcAcknowledgements; // 7 map16 R A + /** + * Indicates whether SetTCAcknowledgements is currently required to be called with the inclusion of mandatory terms + * accepted. + * This attribute may be present and False in the case where no terms and conditions are currently mandatory to + * accept for CommissioningComplete to succeed. + * This attribute may appear, or become True after commissioning (e.g. due to a firmware update) to indicate that + * new Terms & Conditions are available that the user must accept. + * Upon Factory Data Reset, this attribute shall be set to a value of True. + * When Custom Commissioning Flow is used to obtain user consent (e.g. because the Commissioner does not support the + * TC feature), the manufacturer-provided means for obtaining user consent shall ensure that this attribute is set + * to False before returning the user back to the original Commissioner (see Enhanced Setup Flow). + */ + public Boolean tcAcknowledgementsRequired; // 8 bool R A + /** + * Indicates the System Time in seconds when any functionality limitations will begin due to a lack of acceptance of + * updated Terms and Conditions, as described in Section 5.7.4.5, “Presenting Updated Terms and Conditions”. + * A null value indicates that there is no pending deadline for updated TC acceptance. + */ + public Integer tcUpdateDeadline; // 9 uint32 R A + // Structs + + /** + * This structure provides some constant values that may be of use to all commissioners. + */ + public class BasicCommissioningInfo { + /** + * This field shall contain a conservative initial duration (in seconds) to set in the FailSafe for the + * commissioning flow to complete successfully. This may vary depending on the speed or sleepiness of the + * Commissionee. This value, if used in the ArmFailSafe command’s ExpiryLengthSeconds field SHOULD allow a + * Commissioner to proceed with a nominal commissioning without having to-rearm the fail-safe, with some margin. + */ + public Integer failSafeExpiryLengthSeconds; // uint16 + /** + * This field shall contain a conservative value in seconds denoting the maximum total duration for which a fail + * safe timer can be re-armed. See Section 11.10.7.2.1, “Fail Safe Context”. + * The value of this field shall be greater than or equal to the FailSafeExpiryLengthSeconds. Absent additional + * guidelines, it is recommended that the value of this field be aligned with the initial Announcement Duration + * and default to 900 seconds. + */ + public Integer maxCumulativeFailsafeSeconds; // uint16 + + public BasicCommissioningInfo(Integer failSafeExpiryLengthSeconds, Integer maxCumulativeFailsafeSeconds) { + this.failSafeExpiryLengthSeconds = failSafeExpiryLengthSeconds; + this.maxCumulativeFailsafeSeconds = maxCumulativeFailsafeSeconds; + } + } + + // Enums + /** + * This enumeration is used by several response commands in this cluster to indicate particular errors. + */ + public enum CommissioningErrorEnum implements MatterEnum { + OK(0, "Ok"), + VALUE_OUTSIDE_RANGE(1, "Value Outside Range"), + INVALID_AUTHENTICATION(2, "Invalid Authentication"), + NO_FAIL_SAFE(3, "No Fail Safe"), + BUSY_WITH_OTHER_ADMIN(4, "Busy With Other Admin"), + REQUIRED_TC_NOT_ACCEPTED(5, "Required Tc Not Accepted"), + TC_ACKNOWLEDGEMENTS_NOT_RECEIVED(6, "Tc Acknowledgements Not Received"), + TC_MIN_VERSION_NOT_MET(7, "Tc Min Version Not Met"); + + public final Integer value; + public final String label; + + private CommissioningErrorEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration is used by the RegulatoryConfig and LocationCapability attributes to indicate possible radio + * usage. + */ + public enum RegulatoryLocationTypeEnum implements MatterEnum { + INDOOR(0, "Indoor"), + OUTDOOR(1, "Outdoor"), + INDOOR_OUTDOOR(2, "Indoor Outdoor"); + + public final Integer value; + public final String label; + + private RegulatoryLocationTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + /** + * Indicates the user’s response to the presented terms. Each bit position corresponds to a user response for the + * associated index of matching text, such that bit 0 (bit value 1) is for text index 0. Bit 15 (bit value 0x8000) + * is for text index 15. A bit value of 1 indicates acceptance and a value of 0 indicates non-acceptance. For + * example, if there are two texts that were presented where the first (bit 0, value 1) was declined and the second + * accepted (bit 1, value 2), we would expect the resulting value of the map to be 2. + * Whenever a user provides responses to newly presented terms and conditions, this attribute shall be updated with + * the latest responses. This may happen in response to updated terms that were presented to the user. On a factory + * reset this field shall be reset with all bits set to 0. + */ + public static class TcAcknowledgements { + public TcAcknowledgements() { + } + } + + public static class FeatureMap { + /** + * + * Supports Terms & Conditions acknowledgement + */ + public boolean termsAndConditions; + + public FeatureMap(boolean termsAndConditions) { + this.termsAndConditions = termsAndConditions; + } + } + + /** + * This field shall contain the user responses to the Enhanced Setup Flow Terms & Conditions as a map where each + * bit set in the bitmap corresponds to an accepted term in the file located at EnhancedSetupFlowTCUrl. + * ### Effect on Receipt + * This command shall copy the user responses and accepted version to the presented Enhanced Setup Flow Terms & + * Conditions from the values provided in the TCUserResponse and TCVersion fields to the TCAcknowledgements + * Attribute and the TCAcceptedVersion Attribute fields respectively. + * This command shall result in success with an ErrorCode value of OK in the SetTCAcknowledgementsResponse if all + * required terms were accepted by the user. Specifically, all bits have a value of 1 in TCAcknowledgements whose + * ordinal is marked as required in the file located at EnhancedSetupFlowTCUrl. + * If the TCVersion field is less than the TCMinRequiredVersion, then the ErrorCode of TCMinVersionNotMet shall be + * returned and TCAcknowledgements shall remain unchanged. + * If TCVersion is greater than or equal to TCMinRequiredVersion, but the TCUserResponse value indicates that not + * all required terms were accepted by the user, then the ErrorCode of RequiredTCNotAccepted shall be returned and + * TCAcknowledgements shall remain unchanged. + */ + public static class TcUserResponse { + public TcUserResponse() { + } + } + + public GeneralCommissioningCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 48, "GeneralCommissioning"); + } + + protected GeneralCommissioningCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Success or failure of this command shall be communicated by the ArmFailSafeResponse command, unless some data + * model validations caused a failure status code to be issued during the processing of the command. + * If the fail-safe timer is not currently armed, the commissioning window is open, and the command was received + * over a CASE session, the command shall leave the current fail-safe state unchanged and immediately respond with + * an ArmFailSafeResponse containing an ErrorCode value of BusyWithOtherAdmin. This is done to allow commissioners, + * which use PASE connections, the opportunity to use the failsafe during the relatively short commissioning window. + * Otherwise, the command shall arm or re-arm the "fail-safe timer" with an expiry time set for a duration + * of ExpiryLengthSeconds, or disarm it, depending on the situation: + * • If ExpiryLengthSeconds is 0 and the fail-safe timer was already armed and the accessing fabric matches the + * Fabric currently associated with the fail-safe context, then the fail-safe timer shall be immediately expired + * (see further below for side-effects of expiration). + * • If ExpiryLengthSeconds is 0 and the fail-safe timer was not armed, then this command invocation shall lead to a + * success response with no side-effects against the fail-safe context. + * • If ExpiryLengthSeconds is non-zero and the fail-safe timer was not currently armed, then the fail-safe timer + * shall be armed for that duration. + * • If ExpiryLengthSeconds is non-zero and the fail-safe timer was currently armed, and the accessing Fabric + * matches the fail-safe context’s associated Fabric, then the fail-safe timer shall be re-armed to expire in + * ExpiryLengthSeconds. + * • Otherwise, the command shall leave the current fail-safe state unchanged and immediately respond with + * ArmFailSafeResponse containing an ErrorCode value of BusyWithOtherAdmin, indicating a likely conflict between + * commissioners. + * The value of the Breadcrumb field shall be written to the Breadcrumb on successful execution of the command. + * If the receiver restarts unexpectedly (e.g., power interruption, software crash, or other reset) the receiver + * shall behave as if the fail-safe timer expired and perform the sequence of clean-up steps listed below. + * On successful execution of the command, the ErrorCode field of the ArmFailSafeResponse shall be set to OK. + * ### Fail Safe Context + * When first arming the fail-safe timer, a 'Fail Safe Context' shall be created on the receiver, to track + * the following state information while the fail-safe is armed: + * • The fail-safe timer duration. + * • The state of all Network Commissioning Networks attribute configurations, to allow recovery of connectivity + * after Fail-Safe expiry. + * • Whether an AddNOC command or UpdateNOC command has taken place. + * • A Fabric Index for the fabric-scoping of the context, starting at the accessing fabric index for the + * ArmFailSafe command, and updated with the Fabric Index associated with an AddNOC command or an UpdateNOC command + * being invoked successfully during the ongoing Fail-Safe timer period. + * • The operational credentials associated with any Fabric whose configuration is affected by the UpdateNOC + * command. + * • Optionally: the previous state of non-fabric-scoped data that is mutated during the fail-safe period. + * Note the following to assist in understanding the above state-keeping, which summarizes other normative + * requirements in the respective sections: + * • The AddNOC command can only be invoked once per contiguous non-expiring fail-safe timer period, and only if no + * UpdateNOC command was previously processed within the same fail-safe timer period. + * • The UpdateNOC command can only be invoked once per contiguous non-expiring fail-safe timer period, can only be + * invoked over a CASE session, and only if no AddNOC command was previously processed in the same fail-safe timer + * period. + * On creation of the Fail Safe Context a second timer shall be created to expire at MaxCumulativeFailsafeSeconds as + * specified in BasicCommissioningInfo. This Cumulative Fail Safe Context timer (CFSC timer) serves to limit the + * lifetime of any particular Fail Safe Context; it shall NOT be extended or modified on subsequent invocations of + * ArmFailSafe associated with this Fail Safe Context. Upon expiry of the CFSC timer, the receiver shall execute + * cleanup behavior equivalent to that of fail-safe timer expiration as detailed in Section 11.10.7.2.2, “Behavior + * on expiry of Fail-Safe timer”. Termination of the session prior to the expiration of that timer for any reason + * (including a successful end of commissioning or an expiry of a fail-safe timer) shall also delete the CFSC timer. + * ### Behavior on expiry of Fail-Safe timer + * If the fail-safe timer expires before the CommissioningComplete command is successfully invoked, the following + * sequence of clean-up steps shall be executed, in order, by the receiver: + * 1. Terminate any open PASE secure session by clearing any associated Secure Session Context at the Server. + * 2. Revoke the temporary administrative privileges granted to any open PASE session (see Section 6.6.2.9, + * “Bootstrapping of the Access Control Cluster”) at the Server. + * 3. If an AddNOC or UpdateNOC command has been successfully invoked, terminate all CASE sessions associated with + * the Fabric whose Fabric Index is recorded in the Fail-Safe context (see ArmFailSafe) by clearing any associated + * Secure Session Context at the Server. + * 4. Reset the configuration of all Network Commissioning Networks attribute to their state prior to the Fail-Safe + * being armed. + * 5. If an UpdateNOC command had been successfully invoked, revert the state of operational key pair, NOC and ICAC + * for that Fabric to the state prior to the Fail-Safe timer being armed, for the Fabric Index that was the subject + * of the UpdateNOC command. + * 6. If an AddNOC command had been successfully invoked, achieve the equivalent effect of invoking the RemoveFabric + * command against the Fabric Index stored in the Fail-Safe Context for the Fabric Index that was the subject of the + * AddNOC command. This shall remove all associations to that Fabric including all fabric-scoped data, and may + * possibly factory-reset the device depending on current device state. This shall only apply to Fabrics added + * during the fail-safe period as the result of the AddNOC command. + * 7. If the CSRRequest command had been successfully invoked, but no AddNOC or UpdateNOC command had been + * successfully invoked, then the new operational key pair temporarily generated for the purposes of NOC addition or + * update (see Node Operational CSR Procedure) shall be removed as it is no longer needed. + * 8. Remove any RCACs added by the AddTrustedRootCertificate command that are not currently referenced by any entry + * in the Fabrics attribute. + * 9. Reset the Breadcrumb attribute to zero. + * 10. Optionally: if no factory-reset resulted from the previous steps, it is recommended that the Node rollback + * the state of all non fabric-scoped data present in the Fail-Safe context. + */ + public static ClusterCommand armFailSafe(Integer expiryLengthSeconds, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (expiryLengthSeconds != null) { + map.put("expiryLengthSeconds", expiryLengthSeconds); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("armFailSafe", map); + } + + /** + * This shall add or update the regulatory configuration in the RegulatoryConfig Attribute to the value provided in + * the NewRegulatoryConfig field. + * Success or failure of this command shall be communicated by the SetRegulatoryConfigResponse command, unless some + * data model validations caused a failure status code to be issued during the processing of the command. + * The CountryCode field shall conforms to ISO 3166-1 alpha-2 and shall be used to set the Location attribute + * reflected by the Basic Information Cluster. + * If the server limits some of the values (e.g. locked to a particular country, with no regulatory data for + * others), then setting regulatory information outside a valid country or location shall still set the Location + * attribute reflected by the Basic Information Cluster configuration, but the SetRegulatoryConfigResponse replied + * shall have the ErrorCode field set to ValueOutsideRange error. + * If the LocationCapability attribute is not Indoor/Outdoor and the NewRegulatoryConfig value received does not + * match either the Indoor or Outdoor fixed value in LocationCapability, then the SetRegulatoryConfigResponse + * replied shall have the ErrorCode field set to ValueOutsideRange error and the RegulatoryConfig attribute and + * associated internal radio configuration shall remain unchanged. + * If the LocationCapability attribute is set to Indoor/Outdoor, then the RegulatoryConfig attribute shall be set to + * match the NewRegulatoryConfig field. + * On successful execution of the command, the ErrorCode field of the SetRegulatoryConfigResponse shall be set to + * OK. + * The Breadcrumb field shall be used to atomically set the Breadcrumb attribute on success of this command, when + * SetRegulatoryConfigResponse has the ErrorCode field set to OK. If the command fails, the Breadcrumb attribute + * shall be left unchanged. + */ + public static ClusterCommand setRegulatoryConfig(RegulatoryLocationTypeEnum newRegulatoryConfig, String countryCode, + BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (newRegulatoryConfig != null) { + map.put("newRegulatoryConfig", newRegulatoryConfig); + } + if (countryCode != null) { + map.put("countryCode", countryCode); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("setRegulatoryConfig", map); + } + + /** + * This command has no data. + * Success or failure of this command shall be communicated by the CommissioningCompleteResponse command, unless + * some data model validations caused a failure status code to be issued during the processing of the command. + * This command signals the Server that the Commissioner or Administrator has successfully completed all steps + * needed during the Fail-Safe period, such as commissioning (see Section 5.5, “Commissioning Flows”) or other + * Administrator operations requiring usage of the Fail Safe timer. It ensures that the Server is configured in a + * state such that it still has all necessary elements to be fully operable within a Fabric, such as ACL entries + * (see Section 9.10, “Access Control Cluster”) and operational credentials (see Section 6.4, “Node Operational + * Credentials Specification”), and that the Node is reachable using CASE (see Section 4.14.2, “Certificate + * Authenticated Session Establishment (CASE)”) over an operational network. + * An ErrorCode of NoFailSafe shall be responded to the invoker if the CommissioningComplete command was received + * when no Fail-Safe context exists. + * If Terms and Conditions are required, then an ErrorCode of TCAcknowledgementsNotReceived shall be responded to + * the invoker if the user acknowledgements to the required Terms and Conditions have not been provided. If the + * TCAcceptedVersion for the provided acknowledgements is less than TCMinRequiredVersion, then an ErrorCode of + * TCMinVersionNotMet shall be responded to the invoker. + * This command is fabric-scoped, so cannot be issued over a session that does not have an associated fabric, i.e. + * over PASE session prior to an AddNOC command. In addition, this command is only permitted over CASE and must be + * issued by a node associated with the ongoing Fail-Safe context. An ErrorCode of InvalidAuthentication shall be + * responded to the invoker if the CommissioningComplete command was received outside a CASE session (e.g., over + * Group messaging, or PASE session after AddNOC), or if the accessing fabric is not the one associated with the + * ongoing Fail-Safe context. + * This command shall only result in success with an ErrorCode value of OK in the CommissioningCompleteResponse if + * received over a CASE session and the accessing fabric index matches the Fabric Index associated with the current + * Fail-Safe context. In other words: + * • If no AddNOC command had been successfully invoked, the CommissioningComplete command must originate from the + * Fabric that initiated the Fail-Safe context. + * • After an AddNOC command has been successfully invoked, the CommissioningComplete command must originate from + * the Fabric which was joined through the execution of that command, which updated the Fail-Safe context’s Fabric + * Index. + * On successful execution of the CommissioningComplete command, where the CommissioningCompleteResponse has an + * ErrorCode of OK, the following actions shall be undertaken on the Server: + * 1. The Fail-Safe timer associated with the current Fail-Safe context shall be disarmed. + * 2. The commissioning window at the Server shall be closed. + * 3. Any temporary administrative privileges automatically granted to any open PASE session shall be revoked (see + * Section 6.6.2.9, “Bootstrapping of the Access Control Cluster”). + * 4. The Secure Session Context of any PASE session still established at the Server shall be cleared. + * 5. The Breadcrumb attribute shall be reset to zero. + * After receipt of a CommissioningCompleteResponse with an ErrorCode value of OK, a client cannot expect any + * previously established PASE session to still be usable, due to the server having cleared such sessions. + */ + public static ClusterCommand commissioningComplete() { + return new ClusterCommand("commissioningComplete"); + } + + /** + * This command sets the user acknowledgements received in the Enhanced Setup Flow Terms & Conditions into the + * node. + */ + public static ClusterCommand setTcAcknowledgements(Integer tcVersion, TcUserResponse tcUserResponse) { + Map map = new LinkedHashMap<>(); + if (tcVersion != null) { + map.put("tcVersion", tcVersion); + } + if (tcUserResponse != null) { + map.put("tcUserResponse", tcUserResponse); + } + return new ClusterCommand("setTcAcknowledgements", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "breadcrumb : " + breadcrumb + "\n"; + str += "basicCommissioningInfo : " + basicCommissioningInfo + "\n"; + str += "regulatoryConfig : " + regulatoryConfig + "\n"; + str += "locationCapability : " + locationCapability + "\n"; + str += "supportsConcurrentConnection : " + supportsConcurrentConnection + "\n"; + str += "tcAcceptedVersion : " + tcAcceptedVersion + "\n"; + str += "tcMinRequiredVersion : " + tcMinRequiredVersion + "\n"; + str += "tcAcknowledgements : " + tcAcknowledgements + "\n"; + str += "tcAcknowledgementsRequired : " + tcAcknowledgementsRequired + "\n"; + str += "tcUpdateDeadline : " + tcUpdateDeadline + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GeneralDiagnosticsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GeneralDiagnosticsCluster.java new file mode 100644 index 00000000000..dcb6047a0dd --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GeneralDiagnosticsCluster.java @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * GeneralDiagnostics + * + * @author Dan Cunningham - Initial contribution + */ +public class GeneralDiagnosticsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0033; + public static final String CLUSTER_NAME = "GeneralDiagnostics"; + public static final String CLUSTER_PREFIX = "generalDiagnostics"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_NETWORK_INTERFACES = "networkInterfaces"; + public static final String ATTRIBUTE_REBOOT_COUNT = "rebootCount"; + public static final String ATTRIBUTE_UP_TIME = "upTime"; + public static final String ATTRIBUTE_TOTAL_OPERATIONAL_HOURS = "totalOperationalHours"; + public static final String ATTRIBUTE_BOOT_REASON = "bootReason"; + public static final String ATTRIBUTE_ACTIVE_HARDWARE_FAULTS = "activeHardwareFaults"; + public static final String ATTRIBUTE_ACTIVE_RADIO_FAULTS = "activeRadioFaults"; + public static final String ATTRIBUTE_ACTIVE_NETWORK_FAULTS = "activeNetworkFaults"; + public static final String ATTRIBUTE_TEST_EVENT_TRIGGERS_ENABLED = "testEventTriggersEnabled"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The NetworkInterfaces attribute shall be a list of NetworkInterface structs. Each logical network interface on + * the Node shall be represented by a single entry within the NetworkInterfaces attribute. + */ + public List networkInterfaces; // 0 list R V + /** + * The RebootCount attribute shall indicate a best-effort count of the number of times the Node has rebooted. The + * RebootCount attribute SHOULD be incremented each time the Node reboots. The RebootCount attribute shall NOT be + * incremented when a Node wakes from a low-power or sleep state. The RebootCount attribute shall only be reset to 0 + * upon a factory reset of the Node. + */ + public Integer rebootCount; // 1 uint16 R V + /** + * The UpTime attribute shall indicate a best-effort assessment of the length of time, in seconds, since the Node’s + * last reboot. This attribute SHOULD be incremented to account for the periods of time that a Node is in a + * low-power or sleep state. This attribute shall only be reset upon a device reboot. This attribute shall be based + * on the same System Time source as those used to fulfill any usage of the system-us and system-ms data types + * within the server. + */ + public BigInteger upTime; // 2 uint64 R V + /** + * The TotalOperationalHours attribute shall indicate a best-effort attempt at tracking the length of time, in + * hours, that the Node has been operational. The TotalOperationalHours attribute SHOULD be incremented to account + * for the periods of time that a Node is in a low-power or sleep state. The TotalOperationalHours attribute shall + * only be reset upon a factory reset of the Node. + */ + public Integer totalOperationalHours; // 3 uint32 R V + /** + * The BootReason attribute shall indicate the reason for the Node’s most recent boot. + */ + public BootReasonEnum bootReason; // 4 BootReasonEnum R V + /** + * The ActiveHardwareFaults attribute shall indicate the set of faults currently detected by the Node. When the Node + * detects a fault has been raised, the appropriate HardwareFaultEnum value shall be added to this list. This list + * shall NOT contain more than one instance of a specific HardwareFaultEnum value. When the Node detects that all + * conditions contributing to a fault has been cleared, the corresponding HardwareFaultEnum value shall be removed + * from this list. An empty list shall indicate there are currently no active faults. The order of this list SHOULD + * have no significance. Clients interested in monitoring changes in active faults may subscribe to this attribute, + * or they may subscribe to HardwareFaultChange. + */ + public List activeHardwareFaults; // 5 list R V + /** + * The ActiveRadioFaults attribute shall indicate the set of faults currently detected by the Node. When the Node + * detects a fault has been raised, the appropriate RadioFaultEnum value shall be added to this list. This list + * shall NOT contain more than one instance of a specific RadioFaultEnum value. When the Node detects that all + * conditions contributing to a fault has been cleared, the corresponding RadioFaultEnum value shall be removed from + * this list. An empty list shall indicate there are currently no active faults. The order of this list SHOULD have + * no significance. Clients interested in monitoring changes in active faults may subscribe to this attribute, or + * they may subscribe to RadioFaultChange. + */ + public List activeRadioFaults; // 6 list R V + /** + * The ActiveNetworkFaults attribute shall indicate the set of faults currently detected by the Node. When the Node + * detects a fault has been raised, the appropriate NetworkFaultEnum value shall be added to this list. This list + * shall NOT contain more than one instance of a specific NetworkFaultEnum value. When the Node detects that all + * conditions contributing to a fault has been cleared, the corresponding NetworkFaultEnum value shall be removed + * from this list. An empty list shall indicate there are currently no active faults. The order of this list SHOULD + * have no significance. Clients interested in monitoring changes in active faults may subscribe to this attribute, + * or they may subscribe to NetworkFaultChange. + */ + public List activeNetworkFaults; // 7 list R V + /** + * The TestEventTriggersEnabled attribute shall indicate whether the Node has any TestEventTrigger configured. When + * this attribute is true, the Node has been configured with one or more test event triggers by virtue of the + * internally programmed EnableKey value (see TestEventTrigger) being set to a non-zero value. This attribute can be + * used by Administrators to detect if a device was inadvertently commissioned with test event trigger mode enabled, + * and take appropriate action (e.g. warn the user and/or offer to remove all fabrics on the Node). + */ + public Boolean testEventTriggersEnabled; // 8 bool R V + // Structs + + /** + * The HardwareFaultChange Event shall indicate a change in the set of hardware faults currently detected by the + * Node. + */ + public class HardwareFaultChange { + /** + * This field shall represent the set of faults currently detected, as per HardwareFaultEnum. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per HardwareFaultEnum. + */ + public List previous; // list + + public HardwareFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + /** + * The RadioFaultChange Event shall indicate a change in the set of radio faults currently detected by the Node. + */ + public class RadioFaultChange { + /** + * This field shall represent the set of faults currently detected, as per RadioFaultEnum. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per RadioFaultEnum. + */ + public List previous; // list + + public RadioFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + /** + * The NetworkFaultChange Event shall indicate a change in the set of network faults currently detected by the Node. + */ + public class NetworkFaultChange { + /** + * This field shall represent the set of faults currently detected, as per NetworkFaultEnum. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per NetworkFaultEnum. + */ + public List previous; // list + + public NetworkFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + /** + * The BootReason Event shall indicate the reason that caused the device to start-up. + */ + public class BootReason { + /** + * This field shall contain the reason for this BootReason event. + */ + public BootReasonEnum bootReason; // BootReasonEnum + + public BootReason(BootReasonEnum bootReason) { + this.bootReason = bootReason; + } + } + + /** + * This structure describes a network interface supported by the Node, as provided in the NetworkInterfaces + * attribute. + */ + public class NetworkInterface { + /** + * This field shall indicate a human-readable (displayable) name for the network interface, that is different + * from all other interfaces. + */ + public String name; // string + /** + * This field shall indicate if the Node is currently advertising itself operationally on this network interface + * and is capable of successfully receiving incoming traffic from other Nodes. + */ + public Boolean isOperational; // bool + /** + * This field shall indicate whether the Node is currently able to reach off-premise services it uses by + * utilizing IPv4. The value shall be null if the Node does not use such services or does not know whether it + * can reach them. + */ + public Boolean offPremiseServicesReachableIPv4; // bool + /** + * This field shall indicate whether the Node is currently able to reach off-premise services it uses by + * utilizing IPv6. The value shall be null if the Node does not use such services or does not know whether it + * can reach them. + */ + public Boolean offPremiseServicesReachableIPv6; // bool + /** + * This field shall contain the current link-layer address for a 802.3 or IEEE 802.11-2020 network interface and + * contain the current extended MAC address for a 802.15.4 interface. The byte order of the octstr shall be in + * wire byte order. For addresses values less than 64 bits, the first two bytes shall be zero. + */ + public OctetString hardwareAddress; // hwadr + /** + * This field shall provide a list of the IPv4 addresses that are currently assigned to the network interface. + */ + public List iPv4Addresses; // list + /** + * This field shall provide a list of the unicast IPv6 addresses that are currently assigned to the network + * interface. This list shall include the Node’s link-local address and SHOULD include any assigned GUA and ULA + * addresses. This list shall NOT include any multicast group addresses to which the Node is subscribed. + */ + public List iPv6Addresses; // list + /** + * This field shall indicate the type of the interface using the InterfaceTypeEnum. + */ + public InterfaceTypeEnum type; // InterfaceTypeEnum + + public NetworkInterface(String name, Boolean isOperational, Boolean offPremiseServicesReachableIPv4, + Boolean offPremiseServicesReachableIPv6, OctetString hardwareAddress, List iPv4Addresses, + List iPv6Addresses, InterfaceTypeEnum type) { + this.name = name; + this.isOperational = isOperational; + this.offPremiseServicesReachableIPv4 = offPremiseServicesReachableIPv4; + this.offPremiseServicesReachableIPv6 = offPremiseServicesReachableIPv6; + this.hardwareAddress = hardwareAddress; + this.iPv4Addresses = iPv4Addresses; + this.iPv6Addresses = iPv6Addresses; + this.type = type; + } + } + + // Enums + public enum HardwareFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + RADIO(1, "Radio"), + SENSOR(2, "Sensor"), + RESETTABLE_OVER_TEMP(3, "Resettable Over Temp"), + NON_RESETTABLE_OVER_TEMP(4, "Non Resettable Over Temp"), + POWER_SOURCE(5, "Power Source"), + VISUAL_DISPLAY_FAULT(6, "Visual Display Fault"), + AUDIO_OUTPUT_FAULT(7, "Audio Output Fault"), + USER_INTERFACE_FAULT(8, "User Interface Fault"), + NON_VOLATILE_MEMORY_ERROR(9, "Non Volatile Memory Error"), + TAMPER_DETECTED(10, "Tamper Detected"); + + public final Integer value; + public final String label; + + private HardwareFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum RadioFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + WI_FI_FAULT(1, "Wi Fi Fault"), + CELLULAR_FAULT(2, "Cellular Fault"), + THREAD_FAULT(3, "Thread Fault"), + NFC_FAULT(4, "Nfc Fault"), + BLE_FAULT(5, "Ble Fault"), + ETHERNET_FAULT(6, "Ethernet Fault"); + + public final Integer value; + public final String label; + + private RadioFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum NetworkFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + HARDWARE_FAILURE(1, "Hardware Failure"), + NETWORK_JAMMED(2, "Network Jammed"), + CONNECTION_FAILED(3, "Connection Failed"); + + public final Integer value; + public final String label; + + private NetworkFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum InterfaceTypeEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + WI_FI(1, "Wi Fi"), + ETHERNET(2, "Ethernet"), + CELLULAR(3, "Cellular"), + THREAD(4, "Thread"); + + public final Integer value; + public final String label; + + private InterfaceTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BootReasonEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + POWER_ON_REBOOT(1, "Power On Reboot"), + BROWN_OUT_RESET(2, "Brown Out Reset"), + SOFTWARE_WATCHDOG_RESET(3, "Software Watchdog Reset"), + HARDWARE_WATCHDOG_RESET(4, "Hardware Watchdog Reset"), + SOFTWARE_UPDATE_COMPLETED(5, "Software Update Completed"), + SOFTWARE_RESET(6, "Software Reset"); + + public final Integer value; + public final String label; + + private BootReasonEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature indicates support for extended Data Model testing commands, which are required in some + * situations. + * This feature shall be supported if the MaxPathsPerInvoke attribute of the Basic Information Cluster has a + * value > 1. + */ + public boolean dataModelTest; + + public FeatureMap(boolean dataModelTest) { + this.dataModelTest = dataModelTest; + } + } + + public GeneralDiagnosticsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 51, "GeneralDiagnostics"); + } + + protected GeneralDiagnosticsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall be supported to provide a means for certification tests to trigger some test-plan-specific + * events, necessary to assist in automation of device interactions for some certification test cases. This command + * shall NOT cause any changes to the state of the device that persist after the last fabric is removed. + * The fields for the TestEventTrigger command are as follows: + */ + public static ClusterCommand testEventTrigger(OctetString enableKey, BigInteger eventTrigger) { + Map map = new LinkedHashMap<>(); + if (enableKey != null) { + map.put("enableKey", enableKey); + } + if (eventTrigger != null) { + map.put("eventTrigger", eventTrigger); + } + return new ClusterCommand("testEventTrigger", map); + } + + /** + * This command may be used by a client to obtain a correlated view of both System Time, and, if currently + * synchronized and supported, "wall clock time" of the server. This can help clients establish time + * correlation between their concept of time and the server’s concept of time. This is especially useful when + * processing event histories where some events only contain System Time. + * Upon command invocation, the server shall respond with a TimeSnapshotResponse. + */ + public static ClusterCommand timeSnapshot() { + return new ClusterCommand("timeSnapshot"); + } + + /** + * This command provides a means for certification tests or manufacturer’s internal tests to validate particular + * command handling and encoding constraints by generating a response of a given size. + * This command shall use the same EnableKey behavior as the TestEventTrigger command, whereby processing of the + * command is only enabled when the TestEventTriggersEnabled field is true, which shall NOT be true outside of + * certification testing or manufacturer’s internal tests. + * The fields for the PayloadTestRequest command are as follows: + */ + public static ClusterCommand payloadTestRequest(OctetString enableKey, Integer value, Integer count) { + Map map = new LinkedHashMap<>(); + if (enableKey != null) { + map.put("enableKey", enableKey); + } + if (value != null) { + map.put("value", value); + } + if (count != null) { + map.put("count", count); + } + return new ClusterCommand("payloadTestRequest", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "networkInterfaces : " + networkInterfaces + "\n"; + str += "rebootCount : " + rebootCount + "\n"; + str += "upTime : " + upTime + "\n"; + str += "totalOperationalHours : " + totalOperationalHours + "\n"; + str += "bootReason : " + bootReason + "\n"; + str += "activeHardwareFaults : " + activeHardwareFaults + "\n"; + str += "activeRadioFaults : " + activeRadioFaults + "\n"; + str += "activeNetworkFaults : " + activeNetworkFaults + "\n"; + str += "testEventTriggersEnabled : " + testEventTriggersEnabled + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GroupKeyManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GroupKeyManagementCluster.java new file mode 100644 index 00000000000..72d741f3578 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GroupKeyManagementCluster.java @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * GroupKeyManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class GroupKeyManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x003F; + public static final String CLUSTER_NAME = "GroupKeyManagement"; + public static final String CLUSTER_PREFIX = "groupKeyManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_GROUP_KEY_MAP = "groupKeyMap"; + public static final String ATTRIBUTE_GROUP_TABLE = "groupTable"; + public static final String ATTRIBUTE_MAX_GROUPS_PER_FABRIC = "maxGroupsPerFabric"; + public static final String ATTRIBUTE_MAX_GROUP_KEYS_PER_FABRIC = "maxGroupKeysPerFabric"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute is a list of GroupKeyMapStruct entries. Each entry associates a logical Group Id with a particular + * group key set. + */ + public List groupKeyMap; // 0 list RW F VM + /** + * This attribute is a list of GroupInfoMapStruct entries. Each entry provides read-only information about how a + * given logical Group ID maps to a particular set of endpoints, and a name for the group. The content of this + * attribute reflects data managed via the Groups cluster (see AppClusters), and is in general terms referred to as + * the 'node-wide Group Table'. + * The GroupTable shall NOT contain any entry whose GroupInfoMapStruct has an empty Endpoints list. If a RemoveGroup + * or RemoveAllGroups command causes the removal of a group mapping from its last mapped endpoint, the entire + * GroupTable entry for that given GroupId shall be removed. + */ + public List groupTable; // 1 list R F V + /** + * Indicates the maximum number of groups that this node supports per fabric. The value of this attribute shall be + * set to be no less than the required minimum supported groups as specified in Group Limits. The length of the + * GroupKeyMap and GroupTable list attributes shall NOT exceed the value of the MaxGroupsPerFabric attribute + * multiplied by the number of supported fabrics. + */ + public Integer maxGroupsPerFabric; // 2 uint16 R V + /** + * Indicates the maximum number of group key sets this node supports per fabric. The value of this attribute shall + * be set according to the minimum number of group key sets to support as specified in Group Limits. + */ + public Integer maxGroupKeysPerFabric; // 3 uint16 R V + // Structs + + public class GroupKeyMapStruct { + /** + * This field uniquely identifies the group within the scope of the given Fabric. + */ + public Integer groupId; // group-id + /** + * This field references the set of group keys that generate operational group keys for use with this group, as + * specified in Section 4.17.3.5.1, “Group Key Set ID”. + * A GroupKeyMapStruct shall NOT accept GroupKeySetID of 0, which is reserved for the IPK. + */ + public Integer groupKeySetId; // uint16 + public Integer fabricIndex; // FabricIndex + + public GroupKeyMapStruct(Integer groupId, Integer groupKeySetId, Integer fabricIndex) { + this.groupId = groupId; + this.groupKeySetId = groupKeySetId; + this.fabricIndex = fabricIndex; + } + } + + public class GroupKeySetStruct { + /** + * This field shall provide the fabric-unique index for the associated group key set, as specified in Section + * 4.17.3.5.1, “Group Key Set ID”. + */ + public Integer groupKeySetId; // uint16 + /** + * This field shall provide the security policy for an operational group key set. + * When CacheAndSync is not supported in the FeatureMap of this cluster, any action attempting to set + * CacheAndSync in the GroupKeySecurityPolicy field shall fail with an INVALID_COMMAND error. + */ + public GroupKeySecurityPolicyEnum groupKeySecurityPolicy; // GroupKeySecurityPolicyEnum + /** + * This field, if not null, shall be the root credential used in the derivation of an operational group key for + * epoch slot 0 of the given group key set. If EpochKey0 is not null, EpochStartTime0 shall NOT be null. + */ + public OctetString epochKey0; // octstr + /** + * This field, if not null, shall define when EpochKey0 becomes valid as specified by Section 4.17.3, “Epoch + * Keys”. Units are absolute UTC time in microseconds encoded using the epoch-us representation. + */ + public BigInteger epochStartTime0; // epoch-us + /** + * This field, if not null, shall be the root credential used in the derivation of an operational group key for + * epoch slot 1 of the given group key set. If EpochKey1 is not null, EpochStartTime1 shall NOT be null. + */ + public OctetString epochKey1; // octstr + /** + * This field, if not null, shall define when EpochKey1 becomes valid as specified by Section 4.17.3, “Epoch + * Keys”. Units are absolute UTC time in microseconds encoded using the epoch-us representation. + */ + public BigInteger epochStartTime1; // epoch-us + /** + * This field, if not null, shall be the root credential used in the derivation of an operational group key for + * epoch slot 2 of the given group key set. If EpochKey2 is not null, EpochStartTime2 shall NOT be null. + */ + public OctetString epochKey2; // octstr + /** + * This field, if not null, shall define when EpochKey2 becomes valid as specified by Section 4.17.3, “Epoch + * Keys”. Units are absolute UTC time in microseconds encoded using the epoch-us representation. + */ + public BigInteger epochStartTime2; // epoch-us + /** + * This field specifies how the IPv6 Multicast Address shall be formed for groups using this operational group + * key set. + * The PerGroupID method maximizes filtering of multicast messages, so that receiving nodes receive only + * multicast messages for groups to which they are subscribed. + * The AllNodes method minimizes the number of multicast addresses to which a receiver node needs to subscribe. + * > [!NOTE] + * > Support for GroupKeyMulticastPolicy is provisional. Correct default behavior is that implied by value + * PerGroupID. + */ + public GroupKeyMulticastPolicyEnum groupKeyMulticastPolicy; // GroupKeyMulticastPolicyEnum + + public GroupKeySetStruct(Integer groupKeySetId, GroupKeySecurityPolicyEnum groupKeySecurityPolicy, + OctetString epochKey0, BigInteger epochStartTime0, OctetString epochKey1, BigInteger epochStartTime1, + OctetString epochKey2, BigInteger epochStartTime2, + GroupKeyMulticastPolicyEnum groupKeyMulticastPolicy) { + this.groupKeySetId = groupKeySetId; + this.groupKeySecurityPolicy = groupKeySecurityPolicy; + this.epochKey0 = epochKey0; + this.epochStartTime0 = epochStartTime0; + this.epochKey1 = epochKey1; + this.epochStartTime1 = epochStartTime1; + this.epochKey2 = epochKey2; + this.epochStartTime2 = epochStartTime2; + this.groupKeyMulticastPolicy = groupKeyMulticastPolicy; + } + } + + public class GroupInfoMapStruct { + /** + * This field uniquely identifies the group within the scope of the given Fabric. + */ + public Integer groupId; // group-id + /** + * This field provides the list of Endpoint IDs on the Node to which messages to this group shall be forwarded. + */ + public List endpoints; // list + /** + * This field provides a name for the group. This field shall contain the last GroupName written for a given + * GroupId on any Endpoint via the Groups cluster. + */ + public String groupName; // string + public Integer fabricIndex; // FabricIndex + + public GroupInfoMapStruct(Integer groupId, List endpoints, String groupName, Integer fabricIndex) { + this.groupId = groupId; + this.endpoints = endpoints; + this.groupName = groupName; + this.fabricIndex = fabricIndex; + } + } + + // Enums + public enum GroupKeySecurityPolicyEnum implements MatterEnum { + TRUST_FIRST(0, "Trust First"), + CACHE_AND_SYNC(1, "Cache And Sync"); + + public final Integer value; + public final String label; + + private GroupKeySecurityPolicyEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum GroupKeyMulticastPolicyEnum implements MatterEnum { + PER_GROUP_ID(0, "Per Group Id"), + ALL_NODES(1, "All Nodes"); + + public final Integer value; + public final String label; + + private GroupKeyMulticastPolicyEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * The ability to support CacheAndSync security policy and MCSP. + */ + public boolean cacheAndSync; + + public FeatureMap(boolean cacheAndSync) { + this.cacheAndSync = cacheAndSync; + } + } + + public GroupKeyManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 63, "GroupKeyManagement"); + } + + protected GroupKeyManagementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used by Administrators to set the state of a given Group Key Set, including atomically updating + * the state of all epoch keys. + * ### Effect on Receipt + * The following validations shall be done against the content of the GroupKeySet field: + * • If the EpochKey0 field is null or its associated EpochStartTime0 field is null, then this command shall fail + * with an INVALID_COMMAND status code responded to the client. + * • If the EpochKey0 field’s length is not exactly 16 bytes, then this command shall fail with a CONSTRAINT_ERROR + * status code responded to the client. + * • If the EpochStartTime0 is set to 0, then this command shall fail with an INVALID_COMMAND status code responded + * to the client. Note that internally, a GroupKeySetStruct’s EpochStartTime0 may be set to zero, due to the + * behavior of the AddNOC command which synthesizes a GroupKeySetStruct (see IPKValue). However, the value 0 is + * illegal in the GroupKeySet field sent by a client. + * • If the EpochKey1 field is not null, then the EpochKey0 field shall NOT be null. Otherwise this command shall + * fail with an INVALID_COMMAND status code responded to the client. + * • If the EpochKey1 field is not null, and the field’s length is not exactly 16 bytes, then this command shall + * fail with a CONSTRAINT_ERROR status code responded to the client. + * • If the EpochKey1 field is not null, its associated EpochStartTime1 field shall NOT be null and shall contain a + * later epoch start time than the epoch start time found in the EpochStartTime0 field. Otherwise this command shall + * fail with an INVALID_COMMAND status code responded to the client. + * • If exactly one of the EpochKey1 or EpochStartTime1 is null, rather than both being null, or neither being null, + * then this command shall fail with an INVALID_COMMAND status code responded to the client. + * • If the EpochKey2 field is not null, then the EpochKey1 and EpochKey0 fields shall NOT be null. Otherwise this + * command shall fail with an INVALID_COMMAND status code responded to the client. + * • If the EpochKey2 field is not null, and the field’s length is not exactly 16 bytes, then this command shall + * fail with a CONSTRAINT_ERROR status code responded to the client. + * • If the EpochKey2 field is not null, its associated EpochStartTime2 field shall NOT be null and shall contain a + * later epoch start time than the epoch start time found in the EpochStartTime1 field. Otherwise this command shall + * fail with an INVALID_COMMAND status code responded to the client. + * • If exactly one of the EpochKey2 or EpochStartTime2 is null, rather than both being null, or neither being null, + * then this command shall fail with an INVALID_COMMAND status code responded to the client. + * If there exists a Group Key Set associated with the accessing fabric which has the same GroupKeySetID as that + * provided in the GroupKeySet field, then the contents of that group key set shall be replaced. A replacement shall + * be done by executing the equivalent of entirely removing the previous Group Key Set with the given GroupKeySetID, + * followed by an addition of a Group Key Set with the provided configuration. Otherwise, if the GroupKeySetID did + * not match an existing entry, a new Group Key Set associated with the accessing fabric shall be created with the + * provided data. The Group Key Set shall be written to non-volatile storage. + * Upon completion, this command shall send a status code back to the initiator: + * • If the Group Key Set was properly installed or updated on the Node, the status code shall be set to SUCCESS. + * • If there are insufficient resources on the receiver to store an additional Group Key Set, the status code shall + * be set to RESOURCE_EXHAUSTED (see group key limits); + * • Otherwise, this status code shall be set to FAILURE. + */ + public static ClusterCommand keySetWrite(GroupKeySetStruct groupKeySet) { + Map map = new LinkedHashMap<>(); + if (groupKeySet != null) { + map.put("groupKeySet", groupKeySet); + } + return new ClusterCommand("keySetWrite", map); + } + + /** + * This command is used by Administrators to read the state of a given Group Key Set. + * ### Effect on Receipt + * If there exists a Group Key Set associated with the accessing fabric which has the same GroupKeySetID as that + * provided in the GroupKeySetID field, then the contents of that Group Key Set shall be sent in a + * KeySetReadResponse command, but with the EpochKey0, EpochKey1 and EpochKey2 fields replaced by null. + * Otherwise, if the GroupKeySetID does not refer to a Group Key Set associated with the accessing fabric, then this + * command shall fail with a NOT_FOUND status code. + */ + public static ClusterCommand keySetRead(Integer groupKeySetId) { + Map map = new LinkedHashMap<>(); + if (groupKeySetId != null) { + map.put("groupKeySetId", groupKeySetId); + } + return new ClusterCommand("keySetRead", map); + } + + /** + * This command is used by Administrators to remove all state of a given Group Key Set. + * ### Effect on Receipt + * If there exists a Group Key Set associated with the accessing fabric which has the same GroupKeySetID as that + * provided in the GroupKeySetID field, then the contents of that Group Key Set shall be removed, including all + * epoch keys it contains. + * If there exist any entries for the accessing fabric within the GroupKeyMap attribute that refer to the + * GroupKeySetID just removed, then these entries shall be removed from that list. + * This command shall fail with an INVALID_COMMAND status code back to the initiator if the GroupKeySetID being + * removed is 0, which is the Key Set associated with the Identity Protection Key (IPK). The only method to remove + * the IPK is usage of the RemoveFabric command or any operation which causes the equivalent of a RemoveFabric to + * occur by side-effect. + * This command shall send a SUCCESS status code back to the initiator on success, or NOT_FOUND if the GroupKeySetID + * requested did not exist. + */ + public static ClusterCommand keySetRemove(Integer groupKeySetId) { + Map map = new LinkedHashMap<>(); + if (groupKeySetId != null) { + map.put("groupKeySetId", groupKeySetId); + } + return new ClusterCommand("keySetRemove", map); + } + + /** + * This command is used by Administrators to query a list of all Group Key Sets associated with the accessing + * fabric. + * ### Effect on Receipt + * Upon receipt, this command shall iterate all stored GroupKeySetStruct associated with the accessing fabric and + * generate a KeySetReadAllIndicesResponse command containing the list of GroupKeySetID values from those structs. + */ + public static ClusterCommand keySetReadAllIndices() { + return new ClusterCommand("keySetReadAllIndices"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "groupKeyMap : " + groupKeyMap + "\n"; + str += "groupTable : " + groupTable + "\n"; + str += "maxGroupsPerFabric : " + maxGroupsPerFabric + "\n"; + str += "maxGroupKeysPerFabric : " + maxGroupKeysPerFabric + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GroupsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GroupsCluster.java new file mode 100644 index 00000000000..7d498716570 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/GroupsCluster.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * Groups + * + * @author Dan Cunningham - Initial contribution + */ +public class GroupsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0004; + public static final String CLUSTER_NAME = "Groups"; + public static final String CLUSTER_PREFIX = "groups"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_NAME_SUPPORT = "nameSupport"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute provides legacy, read-only access to whether the Group Names feature is supported. The most + * significant bit, bit 7 (GroupNames), shall be equal to bit 0 of the FeatureMap attribute (GN Feature). All other + * bits shall be 0. + */ + public NameSupportBitmap nameSupport; // 0 NameSupportBitmap R V + + // Bitmaps + public static class NameSupportBitmap { + public boolean groupNames; + + public NameSupportBitmap(boolean groupNames) { + this.groupNames = groupNames; + } + } + + public static class FeatureMap { + /** + * + * The Group Names feature indicates the ability to store a name for a group when a group is added. + */ + public boolean groupNames; + + public FeatureMap(boolean groupNames) { + this.groupNames = groupNames; + } + } + + public GroupsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 4, "Groups"); + } + + protected GroupsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * The AddGroup command allows a client to add group membership in a particular group for the server endpoint. + */ + public static ClusterCommand addGroup(Integer groupId, String groupName) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (groupName != null) { + map.put("groupName", groupName); + } + return new ClusterCommand("addGroup", map); + } + + /** + * The ViewGroup command allows a client to request that the server responds with a ViewGroupResponse command + * containing the name string for a particular group. + */ + public static ClusterCommand viewGroup(Integer groupId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + return new ClusterCommand("viewGroup", map); + } + + /** + * The GetGroupMembership command allows a client to inquire about the group membership of the server endpoint, in a + * number of ways. + */ + public static ClusterCommand getGroupMembership(List groupList) { + Map map = new LinkedHashMap<>(); + if (groupList != null) { + map.put("groupList", groupList); + } + return new ClusterCommand("getGroupMembership", map); + } + + /** + * The RemoveGroup command allows a client to request that the server removes the membership for the server + * endpoint, if any, in a particular group. + */ + public static ClusterCommand removeGroup(Integer groupId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + return new ClusterCommand("removeGroup", map); + } + + /** + * The RemoveAllGroups command allows a client to direct the server to remove all group associations for the server + * endpoint. + */ + public static ClusterCommand removeAllGroups() { + return new ClusterCommand("removeAllGroups"); + } + + /** + * The AddGroupIfIdentifying command allows a client to add group membership in a particular group for the server + * endpoint, on condition that the endpoint is identifying itself. Identifying functionality is controlled using the + * Identify cluster, (see Identify Cluster). + * For correct operation of the AddGroupIfIdentifying command, any endpoint that supports the Groups server cluster + * shall also support the Identify server cluster. + * This command might be used to assist configuring group membership in the absence of a commissioning tool. + */ + public static ClusterCommand addGroupIfIdentifying(Integer groupId, String groupName) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (groupName != null) { + map.put("groupName", groupName); + } + return new ClusterCommand("addGroupIfIdentifying", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "nameSupport : " + nameSupport + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/HepaFilterMonitoringCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/HepaFilterMonitoringCluster.java new file mode 100644 index 00000000000..7e11d5963df --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/HepaFilterMonitoringCluster.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * HepaFilterMonitoring + * + * @author Dan Cunningham - Initial contribution + */ +public class HepaFilterMonitoringCluster extends ResourceMonitoringCluster { + + public static final int CLUSTER_ID = 0x0071; + public static final String CLUSTER_NAME = "HepaFilterMonitoring"; + public static final String CLUSTER_PREFIX = "hepaFilterMonitoring"; + + public HepaFilterMonitoringCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 113, "HepaFilterMonitoring"); + } + + protected HepaFilterMonitoringCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IcdManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IcdManagementCluster.java new file mode 100644 index 00000000000..6f588df14c5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IcdManagementCluster.java @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * IcdManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class IcdManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0046; + public static final String CLUSTER_NAME = "IcdManagement"; + public static final String CLUSTER_PREFIX = "icdManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_IDLE_MODE_DURATION = "idleModeDuration"; + public static final String ATTRIBUTE_ACTIVE_MODE_DURATION = "activeModeDuration"; + public static final String ATTRIBUTE_ACTIVE_MODE_THRESHOLD = "activeModeThreshold"; + public static final String ATTRIBUTE_REGISTERED_CLIENTS = "registeredClients"; + public static final String ATTRIBUTE_ICD_COUNTER = "icdCounter"; + public static final String ATTRIBUTE_CLIENTS_SUPPORTED_PER_FABRIC = "clientsSupportedPerFabric"; + public static final String ATTRIBUTE_USER_ACTIVE_MODE_TRIGGER_HINT = "userActiveModeTriggerHint"; + public static final String ATTRIBUTE_USER_ACTIVE_MODE_TRIGGER_INSTRUCTION = "userActiveModeTriggerInstruction"; + public static final String ATTRIBUTE_OPERATING_MODE = "operatingMode"; + public static final String ATTRIBUTE_MAXIMUM_CHECK_IN_BACKOFF = "maximumCheckInBackoff"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the maximum interval in seconds the server can stay in idle mode. The IdleModeDuration shall NOT be + * smaller than the ActiveModeDuration. + */ + public Integer idleModeDuration; // 0 uint32 R V + /** + * Indicates the minimum interval in milliseconds the server typically will stay in active mode after initial + * transition out of idle mode. The ActiveModeDuration does not include the ActiveModeThreshold. + */ + public Integer activeModeDuration; // 1 uint32 R V + /** + * Indicates the minimum amount of time in milliseconds the server typically will stay active after network activity + * when in active mode. + */ + public Integer activeModeThreshold; // 2 uint16 R V + /** + * This attribute shall contain all clients registered to receive notification if their subscription is lost. The + * maximum number of entries that can be in the list shall be ClientsSupportedPerFabric for each fabric supported on + * the server, as indicated by the value of the SupportedFabrics attribute in the Operational Credentials cluster. + */ + public List registeredClients; // 3 list R F A + /** + * This attribute returns the value of the ICD Counter. + */ + public Integer icdCounter; // 4 uint32 R A + /** + * Indicates the maximum number of entries that the server is able to store for each fabric in the RegisteredClients + * attribute. + */ + public Integer clientsSupportedPerFabric; // 5 uint16 R V + /** + * Indicates which user action(s) will trigger the ICD to switch to Active mode. If the attribute indicates support + * for a trigger that is dependent on the UserActiveModeTriggerInstruction in the UserActiveModeTriggerHint table, + * the UserActiveModeTriggerInstruction attribute shall be implemented and shall provide the required information, + * unless specified otherwise in the requirement column of the UserActiveModeTriggerHint table. + * ActuateSensorLightsBlink, ResetButtonLightsBlink and SetupButtonLightsBlink (i.e. bits 7, 9 and 14) have a + * dependency on the UserActiveModeTriggerInstruction attribute but do not require the attribute to be present. + * ### An ICD can indicate multiple ways of being put into Active Mode by setting multiple bits in the bitmap at the + * same time. However, a device shall NOT set more than one bit which has a dependency on the + * UserActiveModeTriggerInstruction attribute. + */ + public UserActiveModeTriggerBitmap userActiveModeTriggerHint; // 6 UserActiveModeTriggerBitmap R V + /** + * The meaning of the attribute is dependent upon the UserActiveModeTriggerHint attribute value, and the conformance + * is in indicated in the "dependency" column in UserActiveModeTriggerHint table. The + * UserActiveModeTriggerInstruction attribute may give additional information on how to transition the device to + * Active Mode. If the attribute is present, the value shall be encoded as a valid UTF-8 string with a maximum + * length of 128 bytes. If the UserActiveModeTriggerHint has the ActuateSensorSeconds, ActuateSensorTimes, + * ResetButtonSeconds, ResetButtonTimes, SetupButtonSeconds or SetupButtonTimes set, the string shall consist solely + * of an encoding of N as a decimal unsigned integer using the ASCII digits 0-9, and without leading zeros. + * For example, given UserActiveModeTriggerHint="2048", ResetButtonTimes is set which indicates + * "Press Reset Button for N seconds". Therefore, a value of + * UserActiveModeTriggerInstruction="10" would indicate that N is 10 in that context. + * When CustomInstruction is set by the UserActiveModeTriggerHint attribute, indicating presence of a custom string, + * the ICD SHOULD perform localization (translation to user’s preferred language, as indicated in the Device’s + * currently configured locale). The Custom Instruction option SHOULD NOT be used by an ICD that does not have + * knowledge of the user’s language preference. + * When the UserActiveModeTriggerHint key indicates a light to blink (ActuateSensorLightsBlink, + * ResetButtonLightsBlink or SetupButtonLightsBlink), information on color of light may be made available via the + * UserActiveModeTriggerInstruction attribute. When using such color indication in the + * UserActiveModeTriggerInstruction attribute, the string shall consist of exactly 6 hexadecimal digits using the + * ASCII characters 0-F and encoding the RGB color value as used in HTML encodings. + */ + public String userActiveModeTriggerInstruction; // 7 string R V + /** + * This attribute shall indicate the operating mode of the ICD as specified in the OperatingModeEnum. + * • If the ICD is operating as a LIT ICD, OperatingMode shall be LIT. + * • If the ICD is operating as a SIT ICD, OperatingMode shall be SIT. + */ + public OperatingModeEnum operatingMode; // 8 OperatingModeEnum R V + /** + * Indicates the maximum time in seconds between two Check-In messages when back-off is active. The + * MaximumCheckInBackoff shall NOT be smaller than the IdleModeDuration. + * If the MaximumCheckInBackoff is equal to the IdleModeDuration, it means the ICD does not back-off. + */ + public Integer maximumCheckInBackoff; // 9 uint32 R V + // Structs + + public class MonitoringRegistrationStruct { + /** + * This field shall indicate the NodeID of the Node to which Check-In messages will be sent when the + * MonitoredSubject is not subscribed. + */ + public BigInteger checkInNodeId; // node-id + /** + * This field shall indicate the monitored Subject ID. This field shall be used to determine if a particular + * client has an active subscription for the given entry. The MonitoredSubject, when it is a NodeID, may be the + * same as the CheckInNodeID. The MonitoredSubject gives the registering client the flexibility of having a + * different CheckInNodeID from the MonitoredSubject. A subscription shall count as an active subscription for + * this entry if: + * • It is on the associated fabric of this entry, and + * • The subject of this entry matches the ISD of the SubscriptionRequest message that created the subscription. + * Matching shall be determined using the subject_matches function defined in the Access Control Privilege + * Granting Algorithm. + * For example, if the MonitoredSubject is Node ID 0x1111_2222_3333_AAAA, and one of the subscribers to the + * server on the entry’s associated fabric bears that Node ID, then the entry matches. + * Another example is if the MonitoredSubject has the value 0xFFFF_FFFD_AA12_0002, and one of the subscribers to + * the server on the entry’s associated fabric bears the CASE Authenticated TAG value 0xAA12 and the version + * 0x0002 or higher within its NOC, then the entry matches. + */ + public BigInteger monitoredSubject; // subject-id + public String key; // + /** + * This field shall indicate the client’s type to inform the ICD of the availability for communication of the + * client. + */ + public ClientTypeEnum clientType; // ClientTypeEnum + public Integer fabricIndex; // FabricIndex + + public MonitoringRegistrationStruct(BigInteger checkInNodeId, BigInteger monitoredSubject, String key, + ClientTypeEnum clientType, Integer fabricIndex) { + this.checkInNodeId = checkInNodeId; + this.monitoredSubject = monitoredSubject; + this.key = key; + this.clientType = clientType; + this.fabricIndex = fabricIndex; + } + } + + // Enums + public enum ClientTypeEnum implements MatterEnum { + PERMANENT(0, "Permanent"), + EPHEMERAL(1, "Ephemeral"); + + public final Integer value; + public final String label; + + private ClientTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum OperatingModeEnum implements MatterEnum { + SIT(0, "Sit"), + LIT(1, "Lit"); + + public final Integer value; + public final String label; + + private OperatingModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + /** + * See the UserActiveModeTriggerHint table for requirements associated to each bit. + */ + public static class UserActiveModeTriggerBitmap { + public boolean powerCycle; + public boolean settingsMenu; + public boolean customInstruction; + public boolean deviceManual; + public boolean actuateSensor; + public boolean actuateSensorSeconds; + public boolean actuateSensorTimes; + public boolean actuateSensorLightsBlink; + public boolean resetButton; + public boolean resetButtonLightsBlink; + public boolean resetButtonSeconds; + public boolean resetButtonTimes; + public boolean setupButton; + public boolean setupButtonSeconds; + public boolean setupButtonLightsBlink; + public boolean setupButtonTimes; + public boolean appDefinedButton; + + public UserActiveModeTriggerBitmap(boolean powerCycle, boolean settingsMenu, boolean customInstruction, + boolean deviceManual, boolean actuateSensor, boolean actuateSensorSeconds, boolean actuateSensorTimes, + boolean actuateSensorLightsBlink, boolean resetButton, boolean resetButtonLightsBlink, + boolean resetButtonSeconds, boolean resetButtonTimes, boolean setupButton, boolean setupButtonSeconds, + boolean setupButtonLightsBlink, boolean setupButtonTimes, boolean appDefinedButton) { + this.powerCycle = powerCycle; + this.settingsMenu = settingsMenu; + this.customInstruction = customInstruction; + this.deviceManual = deviceManual; + this.actuateSensor = actuateSensor; + this.actuateSensorSeconds = actuateSensorSeconds; + this.actuateSensorTimes = actuateSensorTimes; + this.actuateSensorLightsBlink = actuateSensorLightsBlink; + this.resetButton = resetButton; + this.resetButtonLightsBlink = resetButtonLightsBlink; + this.resetButtonSeconds = resetButtonSeconds; + this.resetButtonTimes = resetButtonTimes; + this.setupButton = setupButton; + this.setupButtonSeconds = setupButtonSeconds; + this.setupButtonLightsBlink = setupButtonLightsBlink; + this.setupButtonTimes = setupButtonTimes; + this.appDefinedButton = appDefinedButton; + } + } + + public static class FeatureMap { + /** + * + * When this feature is supported, the device shall support all the associated commands and attributes to + * properly support the Check-In Protocol. + */ + public boolean checkInProtocolSupport; + /** + * + * This feature is supported if and only if the device has a user active mode trigger. + */ + public boolean userActiveModeTrigger; + /** + * + * This feature is supported if and only the device is a Long Idle Time ICD. + */ + public boolean longIdleTimeSupport; + /** + * + * This feature is supported if and only if the device can switch between SIT and LIT operating modes even if it + * has a valid registered client. See the dynamic SIT / LIT operating mode switching for more details. + */ + public boolean dynamicSitLitSupport; + + public FeatureMap(boolean checkInProtocolSupport, boolean userActiveModeTrigger, boolean longIdleTimeSupport, + boolean dynamicSitLitSupport) { + this.checkInProtocolSupport = checkInProtocolSupport; + this.userActiveModeTrigger = userActiveModeTrigger; + this.longIdleTimeSupport = longIdleTimeSupport; + this.dynamicSitLitSupport = dynamicSitLitSupport; + } + } + + public IcdManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 70, "IcdManagement"); + } + + protected IcdManagementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command allows a client to register itself with the ICD to be notified when the device is available for + * communication. + */ + public static ClusterCommand registerClient(BigInteger checkInNodeId, BigInteger monitoredSubject, OctetString key, + OctetString verificationKey, ClientTypeEnum clientType) { + Map map = new LinkedHashMap<>(); + if (checkInNodeId != null) { + map.put("checkInNodeId", checkInNodeId); + } + if (monitoredSubject != null) { + map.put("monitoredSubject", monitoredSubject); + } + if (key != null) { + map.put("key", key); + } + if (verificationKey != null) { + map.put("verificationKey", verificationKey); + } + if (clientType != null) { + map.put("clientType", clientType); + } + return new ClusterCommand("registerClient", map); + } + + /** + * This command allows a client to unregister itself with the ICD. Example: a client that is leaving the network + * (e.g. running on a phone which is leaving the home) can (and should) remove its subscriptions and send this + * UnregisterClient command before leaving to prevent the burden on the ICD of an absent client. + */ + public static ClusterCommand unregisterClient(BigInteger checkInNodeId, OctetString verificationKey) { + Map map = new LinkedHashMap<>(); + if (checkInNodeId != null) { + map.put("checkInNodeId", checkInNodeId); + } + if (verificationKey != null) { + map.put("verificationKey", verificationKey); + } + return new ClusterCommand("unregisterClient", map); + } + + /** + * This command allows a client to request that the server stays in active mode for at least a given time duration + * (in milliseconds) from when this command is received. + * This StayActiveDuration may be longer than the ActiveModeThreshold value and would, typically, be used by the + * client to request the server to stay active and responsive for this period to allow a sequence of message + * exchanges during that period. The client may slightly overestimate the duration it wants the ICD to be active + * for, in order to account for network delays. + */ + public static ClusterCommand stayActiveRequest(Integer stayActiveDuration) { + Map map = new LinkedHashMap<>(); + if (stayActiveDuration != null) { + map.put("stayActiveDuration", stayActiveDuration); + } + return new ClusterCommand("stayActiveRequest", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "idleModeDuration : " + idleModeDuration + "\n"; + str += "activeModeDuration : " + activeModeDuration + "\n"; + str += "activeModeThreshold : " + activeModeThreshold + "\n"; + str += "registeredClients : " + registeredClients + "\n"; + str += "icdCounter : " + icdCounter + "\n"; + str += "clientsSupportedPerFabric : " + clientsSupportedPerFabric + "\n"; + str += "userActiveModeTriggerHint : " + userActiveModeTriggerHint + "\n"; + str += "userActiveModeTriggerInstruction : " + userActiveModeTriggerInstruction + "\n"; + str += "operatingMode : " + operatingMode + "\n"; + str += "maximumCheckInBackoff : " + maximumCheckInBackoff + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IdentifyCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IdentifyCluster.java new file mode 100644 index 00000000000..0e20bd05c2e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IdentifyCluster.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * Identify + * + * @author Dan Cunningham - Initial contribution + */ +public class IdentifyCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0003; + public static final String CLUSTER_NAME = "Identify"; + public static final String CLUSTER_PREFIX = "identify"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_IDENTIFY_TIME = "identifyTime"; + public static final String ATTRIBUTE_IDENTIFY_TYPE = "identifyType"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the remaining length of time, in seconds, that the endpoint will continue to identify itself. + * If this attribute is set to a value other than 0 then the device shall enter its identification state, in order + * to indicate to an observer which of several nodes and/or endpoints it is. It is recommended that this state + * consists of flashing a light with a period of 0.5 seconds. The IdentifyTime attribute shall be decremented every + * second while in this state. + * If this attribute reaches or is set to the value 0 then the device shall terminate its identification state. + */ + public Integer identifyTime; // 0 uint16 RW VO + /** + * Indicates how the identification state is presented to the user. + * This attribute shall contain one of the values defined in IdentifyTypeEnum. The value None shall NOT be used if + * the device is capable of presenting its identification state using one of the other methods defined in + * IdentifyTypeEnum. + */ + public IdentifyTypeEnum identifyType; // 1 IdentifyTypeEnum R V + + // Enums + public enum IdentifyTypeEnum implements MatterEnum { + NONE(0, "None"), + LIGHT_OUTPUT(1, "Light Output"), + VISIBLE_INDICATOR(2, "Visible Indicator"), + AUDIBLE_BEEP(3, "Audible Beep"), + DISPLAY(4, "Display"), + ACTUATOR(5, "Actuator"); + + public final Integer value; + public final String label; + + private IdentifyTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EffectIdentifierEnum implements MatterEnum { + BLINK(0, "Blink"), + BREATHE(1, "Breathe"), + OKAY(2, "Okay"), + CHANNEL_CHANGE(11, "Channel Change"), + FINISH_EFFECT(254, "Finish Effect"), + STOP_EFFECT(255, "Stop Effect"); + + public final Integer value; + public final String label; + + private EffectIdentifierEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EffectVariantEnum implements MatterEnum { + DEFAULT(0, "Default"); + + public final Integer value; + public final String label; + + private EffectVariantEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public IdentifyCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 3, "Identify"); + } + + protected IdentifyCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command starts or stops the receiving device identifying itself. + */ + public static ClusterCommand identify(Integer identifyTime) { + Map map = new LinkedHashMap<>(); + if (identifyTime != null) { + map.put("identifyTime", identifyTime); + } + return new ClusterCommand("identify", map); + } + + /** + * This command allows the support of feedback to the user, such as a certain light effect. It is used to allow an + * implementation to provide visual feedback to the user under certain circumstances such as a color light turning + * green when it has successfully connected to a network. The use of this command and the effects themselves are + * entirely up to the implementer to use whenever a visual feedback is useful but it is not the same as and does not + * replace the identify mechanism used during commissioning. + */ + public static ClusterCommand triggerEffect(EffectIdentifierEnum effectIdentifier, EffectVariantEnum effectVariant) { + Map map = new LinkedHashMap<>(); + if (effectIdentifier != null) { + map.put("effectIdentifier", effectIdentifier); + } + if (effectVariant != null) { + map.put("effectVariant", effectVariant); + } + return new ClusterCommand("triggerEffect", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "identifyTime : " + identifyTime + "\n"; + str += "identifyType : " + identifyType + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IlluminanceMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IlluminanceMeasurementCluster.java new file mode 100644 index 00000000000..c519c684688 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/IlluminanceMeasurementCluster.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * IlluminanceMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class IlluminanceMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0400; + public static final String CLUSTER_NAME = "IlluminanceMeasurement"; + public static final String CLUSTER_PREFIX = "illuminanceMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_MEASURED_VALUE = "measuredValue"; + public static final String ATTRIBUTE_MIN_MEASURED_VALUE = "minMeasuredValue"; + public static final String ATTRIBUTE_MAX_MEASURED_VALUE = "maxMeasuredValue"; + public static final String ATTRIBUTE_TOLERANCE = "tolerance"; + public static final String ATTRIBUTE_LIGHT_SENSOR_TYPE = "lightSensorType"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the illuminance in Lux (symbol lx) as follows: + * • MeasuredValue = 10,000 x log10(illuminance) + 1, + * where 1 lx <= illuminance <= 3.576 Mlx, corresponding to a MeasuredValue in the range 1 to + * 0xFFFE. The MeasuredValue attribute can take the following values: + * • 0 indicates a value of illuminance that is too low to be measured, + * • MinMeasuredValue <= MeasuredValue <= MaxMeasuredValue under normal circumstances, + * • null indicates that the illuminance measurement is invalid. + * The MeasuredValue attribute is updated continuously as new measurements are made. + */ + public Integer measuredValue; // 0 uint16 R V + /** + * Indicates the minimum value of MeasuredValue that can be measured. A value of null indicates that this attribute + * is not defined. See Measured Value for more details. + */ + public Integer minMeasuredValue; // 1 uint16 R V + /** + * Indicates the maximum value of MeasuredValue that can be measured. A value of null indicates that this attribute + * is not defined. See Measured Value for more details. + */ + public Integer maxMeasuredValue; // 2 uint16 R V + /** + * See Measured Value. + */ + public Integer tolerance; // 3 uint16 R V + /** + * Indicates the electronic type of the light sensor. This attribute shall be set to one of the non-reserved values + * listed in LightSensorTypeEnum or null in case the sensor type is unknown. + */ + public Integer lightSensorType; // 4 uint8 R V + + // Enums + public enum LightSensorTypeEnum implements MatterEnum { + PHOTODIODE(0, "Photodiode"), + CMOS(1, "Cmos"); + + public final Integer value; + public final String label; + + private LightSensorTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public IlluminanceMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1024, "IlluminanceMeasurement"); + } + + protected IlluminanceMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "measuredValue : " + measuredValue + "\n"; + str += "minMeasuredValue : " + minMeasuredValue + "\n"; + str += "maxMeasuredValue : " + maxMeasuredValue + "\n"; + str += "tolerance : " + tolerance + "\n"; + str += "lightSensorType : " + lightSensorType + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/JointFabricDatastoreCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/JointFabricDatastoreCluster.java new file mode 100644 index 00000000000..3d3af5b0c58 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/JointFabricDatastoreCluster.java @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * JointFabricDatastore + * + * @author Dan Cunningham - Initial contribution + */ +public class JointFabricDatastoreCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0752; + public static final String CLUSTER_NAME = "JointFabricDatastore"; + public static final String CLUSTER_PREFIX = "jointFabricDatastore"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_ANCHOR_ROOT_CA = "anchorRootCa"; + public static final String ATTRIBUTE_ANCHOR_NODE_ID = "anchorNodeId"; + public static final String ATTRIBUTE_ANCHOR_VENDOR_ID = "anchorVendorId"; + public static final String ATTRIBUTE_FRIENDLY_NAME = "friendlyName"; + public static final String ATTRIBUTE_GROUP_KEY_SET_LIST = "groupKeySetList"; + public static final String ATTRIBUTE_GROUP_LIST = "groupList"; + public static final String ATTRIBUTE_NODE_LIST = "nodeList"; + public static final String ATTRIBUTE_ADMIN_LIST = "adminList"; + public static final String ATTRIBUTE_STATUS_ENTRY = "statusEntry"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This shall indicate the Anchor Root CA used to sign all NOC Issuers in the Joint Fabric. A null value indicates + * that the Joint Fabric is not yet formed. + */ + public OctetString anchorRootCa; // 0 octstr R S A + /** + * This shall indicate the Node identifier of the Joint Fabric Anchor Root CA. + */ + public BigInteger anchorNodeId; // 1 node-id R S A + /** + * This shall indicate the Vendor identifier of the Joint Fabric Anchor Root CA. + */ + public Integer anchorVendorId; // 2 vendor-id R S A + /** + * Friendly name for this fabric which can be propagated to nodes. + */ + public String friendlyName; // 3 string R S A + /** + * This shall indicate the list of GroupKeySetStruct used in the Joint Fabric. + * This attribute shall contain at least one entry, the IPK, which has GroupKeySetID of 0. + */ + public List groupKeySetList; // 4 list R S A + /** + * This shall indicate the list of groups in the Joint Fabric. + */ + public List groupList; // 5 list R S A + /** + * This shall indicate the list of nodes in the Joint Fabric. + */ + public List nodeList; // 6 list R S A + /** + * This shall indicate the list of administrators in the Joint Fabric. + * Only one Administrator may serve as the Anchor Root CA and Anchor Fabric Administrator and shall have index value + * 0. All other Joint Fabric Administrators shall be referenced at index 1 or greater. + * A null value or empty list indicates that the Joint Fabric is not yet formed. + */ + public List adminList; // 7 list R S A + /** + * This shall indicate the current state of the Joint Fabric Datastore Cluster. + * The Committed status indicates the DataStore is ready for use. The Pending status indicates that the DataStore is + * not yet ready for use. The DeletePending status indicates that the DataStore is in the process of being + * transferred to another Joint Fabric Anchor Administrator. + */ + public DatastoreAdministratorInformationEntry statusEntry; // 8 DatastoreAdministratorInformationEntry R S A + // Structs + + public class DatastoreStatusEntry { + /** + * This field shall contain the current state of the target device operation. + */ + public DatastoreStateEnum state; // DatastoreStateEnum + /** + * This field shall contain the timestamp of the last update. + */ + public Integer updateTimestamp; // epoch-s + public Integer fabricIndex; // FabricIndex + + public DatastoreStatusEntry(DatastoreStateEnum state, Integer updateTimestamp, Integer fabricIndex) { + this.state = state; + this.updateTimestamp = updateTimestamp; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreNodeKeyEntry { + public Integer groupKeySetId; // uint16 + /** + * Indicates whether entry in this list is pending, committed, or delete-pending. + */ + public DatastoreStatusEntry statusEntry; // DatastoreStatusEntry + public Integer fabricIndex; // FabricIndex + + public DatastoreNodeKeyEntry(Integer groupKeySetId, DatastoreStatusEntry statusEntry, Integer fabricIndex) { + this.groupKeySetId = groupKeySetId; + this.statusEntry = statusEntry; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreGroupInformationEntry { + /** + * The unique identifier for the group. + */ + public BigInteger groupId; // uint64 + /** + * The friendly name for the group. + */ + public String friendlyName; // string + /** + * The unique identifier for the group key set. + */ + public Integer groupKeySetId; // uint16 + /** + * CAT value for this group. This is used for control of individual members of a group (non-broadcast commands). + */ + public Integer groupCat; // uint16 + /** + * Current version number for this CAT. + */ + public Integer groupCatVersion; // uint16 + /** + * The permission level associated with ACL entries for this group. There should be only one Administrator group + * per fabric, and at most one Manage group per Ecosystem (Vendor Entry). + */ + public AccessControlCluster.AccessControlEntryPrivilegeEnum groupPermission; // AccessControl.AccessControlEntryPrivilegeEnum + public Integer fabricIndex; // FabricIndex + + public DatastoreGroupInformationEntry(BigInteger groupId, String friendlyName, Integer groupKeySetId, + Integer groupCat, Integer groupCatVersion, + AccessControlCluster.AccessControlEntryPrivilegeEnum groupPermission, Integer fabricIndex) { + this.groupId = groupId; + this.friendlyName = friendlyName; + this.groupKeySetId = groupKeySetId; + this.groupCat = groupCat; + this.groupCatVersion = groupCatVersion; + this.groupPermission = groupPermission; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreBindingEntry { + /** + * The unique identifier for the Binding entry in the Datastore’s list of DatastoreBindingEntry. + */ + public Integer listId; // uint16 + /** + * The binding target structure. + */ + public BindingCluster.TargetStruct binding; // Binding.TargetStruct + /** + * Indicates whether entry in this list is pending, committed, or delete-pending. + */ + public DatastoreStatusEntry statusEntry; // DatastoreStatusEntry + public Integer fabricIndex; // FabricIndex + + public DatastoreBindingEntry(Integer listId, BindingCluster.TargetStruct binding, + DatastoreStatusEntry statusEntry, Integer fabricIndex) { + this.listId = listId; + this.binding = binding; + this.statusEntry = statusEntry; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreGroupIDEntry { + /** + * The unique identifier for the group. + */ + public Integer groupId; // group-id + /** + * Indicates whether entry in this list is pending, committed, or delete-pending. + */ + public DatastoreStatusEntry statusEntry; // DatastoreStatusEntry + public Integer fabricIndex; // FabricIndex + + public DatastoreGroupIDEntry(Integer groupId, DatastoreStatusEntry statusEntry, Integer fabricIndex) { + this.groupId = groupId; + this.statusEntry = statusEntry; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreEndpointEntry { + /** + * The unique identifier for the endpoint. + */ + public Integer endpointId; // endpoint-no + /** + * The unique identifier for the node. + */ + public BigInteger nodeId; // node-id + /** + * Friendly name for this endpoint which is propagated to nodes. Any changes to Friendly Name or Group Id List + * (add/remove entry) must follow the pending→committed workflow with current state reflected in the Status + * Entry. + */ + public String friendlyName; // string + /** + * Indicates whether changes to Friendly Name are pending or committed. + */ + public DatastoreStatusEntry statusEntry; // DatastoreStatusEntry + /** + * List of Group IDs that this endpoint is a member of. Any changes to Group Id List (add/remove entry) must + * follow the pending→committed workflow with current state reflected in the Status Entry. + */ + public List groupIdList; // list + /** + * List of Binding Targets for this endpoint. Any changes to Binding List (add/remove entry) must follow the + * pending→committed workflow with current state reflected in the Status Entry. + */ + public List bindingList; // list + public Integer fabricIndex; // FabricIndex + + public DatastoreEndpointEntry(Integer endpointId, BigInteger nodeId, String friendlyName, + DatastoreStatusEntry statusEntry, List groupIdList, + List bindingList, Integer fabricIndex) { + this.endpointId = endpointId; + this.nodeId = nodeId; + this.friendlyName = friendlyName; + this.statusEntry = statusEntry; + this.groupIdList = groupIdList; + this.bindingList = bindingList; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreACLEntry { + /** + * The unique identifier for the ACL entry in the Datastore’s list of DatastoreACLEntry. + */ + public Integer listId; // uint16 + /** + * The Access Control Entry structure. + */ + public AccessControlCluster.AccessControlEntryStruct aclEntry; // AccessControl.AccessControlEntryStruct + /** + * Indicates whether entry in this list is pending, committed, or delete-pending. + */ + public DatastoreStatusEntry statusEntry; // DatastoreStatusEntry + public Integer fabricIndex; // FabricIndex + + public DatastoreACLEntry(Integer listId, AccessControlCluster.AccessControlEntryStruct aclEntry, + DatastoreStatusEntry statusEntry, Integer fabricIndex) { + this.listId = listId; + this.aclEntry = aclEntry; + this.statusEntry = statusEntry; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreNodeInformationEntry { + /** + * The unique identifier for the node. + */ + public BigInteger nodeId; // node-id + /** + * Friendly name for this node which is not propagated to nodes. + */ + public String friendlyName; // string + /** + * Set to pending prior to completing commissioning, and set to completed after commissioning complete is + * successful. + */ + public DatastoreStatusEntry commissioningStatusEntry; // DatastoreStatusEntry + /** + * List of Key Set information for the given Node. Updates to the Group Key List must follow the + * pending→committed workflow with current state reflected in the Status Entry for the corresponding entry in + * the list. + */ + public List nodeKeySetList; // list + /** + * List of ACL entries. Group membership for this node is inferred from the ACLs. Client access to a Node + * Information Entry will be determined from the ACL List. Any changes to ACL List (add/remove entry) must + * follow the pending→committed workflow with current state reflected in the Status Entry for the corresponding + * entry in the list. + */ + public List aclList; // list + /** + * The list of endpoints for this node. Any changes to Endpoint List (add/remove entry) must follow the + * pending→committed workflow with current state reflected in the Status Entry for the corresponding entry in + * the list. + */ + public List endpointList; // list + public Integer fabricIndex; // FabricIndex + + public DatastoreNodeInformationEntry(BigInteger nodeId, String friendlyName, + DatastoreStatusEntry commissioningStatusEntry, List nodeKeySetList, + List aclList, List endpointList, Integer fabricIndex) { + this.nodeId = nodeId; + this.friendlyName = friendlyName; + this.commissioningStatusEntry = commissioningStatusEntry; + this.nodeKeySetList = nodeKeySetList; + this.aclList = aclList; + this.endpointList = endpointList; + this.fabricIndex = fabricIndex; + } + } + + public class DatastoreAdministratorInformationEntry { + /** + * The unique identifier for the node. + */ + public BigInteger nodeId; // node-id + /** + * Friendly name for this node which is not propagated to nodes. + */ + public String friendlyName; // string + /** + * The Vendor ID for the node. + */ + public Integer vendorId; // vendor-id + /** + * The ICAC used to issue the NOC. + */ + public OctetString icac; // octstr + public Integer fabricIndex; // FabricIndex + + public DatastoreAdministratorInformationEntry(BigInteger nodeId, String friendlyName, Integer vendorId, + OctetString icac, Integer fabricIndex) { + this.nodeId = nodeId; + this.friendlyName = friendlyName; + this.vendorId = vendorId; + this.icac = icac; + this.fabricIndex = fabricIndex; + } + } + + // Enums + public enum DatastoreStateEnum implements MatterEnum { + PENDING(0, "Pending"), + COMMITTED(1, "Committed"), + DELETE_PENDING(2, "Delete Pending"); + + public final Integer value; + public final String label; + + private DatastoreStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public JointFabricDatastoreCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1874, "JointFabricDatastore"); + } + + protected JointFabricDatastoreCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand section112471() { + return new ClusterCommand("section112471"); + } + + public static ClusterCommand section112472() { + return new ClusterCommand("section112472"); + } + + public static ClusterCommand section112473() { + return new ClusterCommand("section112473"); + } + + public static ClusterCommand section112474() { + return new ClusterCommand("section112474"); + } + + public static ClusterCommand section112475() { + return new ClusterCommand("section112475"); + } + + public static ClusterCommand section112476() { + return new ClusterCommand("section112476"); + } + + public static ClusterCommand section112477() { + return new ClusterCommand("section112477"); + } + + public static ClusterCommand section112478() { + return new ClusterCommand("section112478"); + } + + public static ClusterCommand section112479() { + return new ClusterCommand("section112479"); + } + + public static ClusterCommand section1124710() { + return new ClusterCommand("section1124710"); + } + + public static ClusterCommand section1124711() { + return new ClusterCommand("section1124711"); + } + + public static ClusterCommand section1124712() { + return new ClusterCommand("section1124712"); + } + + public static ClusterCommand section1124713() { + return new ClusterCommand("section1124713"); + } + + public static ClusterCommand section1124714() { + return new ClusterCommand("section1124714"); + } + + public static ClusterCommand section1124715() { + return new ClusterCommand("section1124715"); + } + + public static ClusterCommand section1124716() { + return new ClusterCommand("section1124716"); + } + + public static ClusterCommand section1124717() { + return new ClusterCommand("section1124717"); + } + + public static ClusterCommand section1124718() { + return new ClusterCommand("section1124718"); + } + + public static ClusterCommand section1124719() { + return new ClusterCommand("section1124719"); + } + + public static ClusterCommand section1124720() { + return new ClusterCommand("section1124720"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "anchorRootCa : " + anchorRootCa + "\n"; + str += "anchorNodeId : " + anchorNodeId + "\n"; + str += "anchorVendorId : " + anchorVendorId + "\n"; + str += "friendlyName : " + friendlyName + "\n"; + str += "groupKeySetList : " + groupKeySetList + "\n"; + str += "groupList : " + groupList + "\n"; + str += "nodeList : " + nodeList + "\n"; + str += "adminList : " + adminList + "\n"; + str += "statusEntry : " + statusEntry + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/JointFabricPkiCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/JointFabricPkiCluster.java new file mode 100644 index 00000000000..312861c9e30 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/JointFabricPkiCluster.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * JointFabricPki + * + * @author Dan Cunningham - Initial contribution + */ +public class JointFabricPkiCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0753; + public static final String CLUSTER_NAME = "JointFabricPki"; + public static final String CLUSTER_PREFIX = "jointFabricPki"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + + // Enums + /** + * This enumeration is used by the ICACSRResponse command to convey the detailed outcome of this cluster’s + * ICACSRRequest command. + */ + public enum IcacsrRequestStatusEnum implements MatterEnum { + OK(0, "Ok"), + INVALID_ICA_CSR_FORMAT(1, "Invalid Ica Csr Format"), + INVALID_ICA_CSR_SIGNATURE(2, "Invalid Ica Csr Signature"), + FAILED_DCL_VENDOR_ID_VALIDATION(3, "Failed Dcl Vendor Id Validation"), + NOT_AN_ICAC(4, "Not An Icac"), + BUSY_ANCHOR_TRANSFER(5, "Busy Anchor Transfer"), + ICA_CSR_SIGNING_FAILED(6, "Ica Csr Signing Failed"), + ICA_CSR_REQUEST_NO_USER_CONSENT(7, "Ica Csr Request No User Consent"); + + public final Integer value; + public final String label; + + private IcacsrRequestStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration is used by the TransferAnchorResponse command to convey the detailed outcome of this cluster’s + * TransferAnchorRequest command. + */ + public enum TransferAnchorResponseStatusEnum implements MatterEnum { + OK(0, "Ok"), + TRANSFER_ANCHOR_STATUS_DATASTORE_BUSY(1, "Transfer Anchor Status Datastore Busy"), + TRANSFER_ANCHOR_STATUS_NO_USER_CONSENT(2, "Transfer Anchor Status No User Consent"); + + public final Integer value; + public final String label; + + private TransferAnchorResponseStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public JointFabricPkiCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1875, "JointFabricPki"); + } + + protected JointFabricPkiCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall be generated and executed during the Joint Commissioning Method steps and subsequently respond + * in the form of an ICACSRResponse command. + * Check ICA Cross Signing for details about the generation and contents of the ICACSR. + */ + public static ClusterCommand icacsrRequest(OctetString icacsr) { + Map map = new LinkedHashMap<>(); + if (icacsr != null) { + map.put("icacsr", icacsr); + } + return new ClusterCommand("icacsrRequest", map); + } + + public static ClusterCommand transferAnchorRequest() { + return new ClusterCommand("transferAnchorRequest"); + } + + public static ClusterCommand transferAnchorComplete() { + return new ClusterCommand("transferAnchorComplete"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/KeypadInputCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/KeypadInputCluster.java new file mode 100644 index 00000000000..98d064fa4fa --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/KeypadInputCluster.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * KeypadInput + * + * @author Dan Cunningham - Initial contribution + */ +public class KeypadInputCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0509; + public static final String CLUSTER_NAME = "KeypadInput"; + public static final String CLUSTER_PREFIX = "keypadInput"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + + // Enums + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + UNSUPPORTED_KEY(1, "Unsupported Key"), + INVALID_KEY_IN_CURRENT_STATE(2, "Invalid Key In Current State"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum CecKeyCodeEnum implements MatterEnum { + SELECT(0, "Select"), + UP(1, "Up"), + DOWN(2, "Down"), + LEFT(3, "Left"), + RIGHT(4, "Right"), + RIGHT_UP(5, "Right Up"), + RIGHT_DOWN(6, "Right Down"), + LEFT_UP(7, "Left Up"), + LEFT_DOWN(8, "Left Down"), + ROOT_MENU(9, "Root Menu"), + SETUP_MENU(10, "Setup Menu"), + CONTENTS_MENU(11, "Contents Menu"), + FAVORITE_MENU(12, "Favorite Menu"), + EXIT(13, "Exit"), + MEDIA_TOP_MENU(16, "Media Top Menu"), + MEDIA_CONTEXT_SENSITIVE_MENU(17, "Media Context Sensitive Menu"), + NUMBER_ENTRY_MODE(29, "Number Entry Mode"), + NUMBER11(30, "Number 11"), + NUMBER12(31, "Number 12"), + NUMBER0OR_NUMBER10(32, "Number 0 Or Number 10"), + NUMBERS1(33, "Numbers 1"), + NUMBERS2(34, "Numbers 2"), + NUMBERS3(35, "Numbers 3"), + NUMBERS4(36, "Numbers 4"), + NUMBERS5(37, "Numbers 5"), + NUMBERS6(38, "Numbers 6"), + NUMBERS7(39, "Numbers 7"), + NUMBERS8(40, "Numbers 8"), + NUMBERS9(41, "Numbers 9"), + DOT(42, "Dot"), + ENTER(43, "Enter"), + CLEAR(44, "Clear"), + NEXT_FAVORITE(47, "Next Favorite"), + CHANNEL_UP(48, "Channel Up"), + CHANNEL_DOWN(49, "Channel Down"), + PREVIOUS_CHANNEL(50, "Previous Channel"), + SOUND_SELECT(51, "Sound Select"), + INPUT_SELECT(52, "Input Select"), + DISPLAY_INFORMATION(53, "Display Information"), + HELP(54, "Help"), + PAGE_UP(55, "Page Up"), + PAGE_DOWN(56, "Page Down"), + POWER(64, "Power"), + VOLUME_UP(65, "Volume Up"), + VOLUME_DOWN(66, "Volume Down"), + MUTE(67, "Mute"), + PLAY(68, "Play"), + STOP(69, "Stop"), + PAUSE(70, "Pause"), + RECORD(71, "Record"), + REWIND(72, "Rewind"), + FAST_FORWARD(73, "Fast Forward"), + EJECT(74, "Eject"), + FORWARD(75, "Forward"), + BACKWARD(76, "Backward"), + STOP_RECORD(77, "Stop Record"), + PAUSE_RECORD(78, "Pause Record"), + ANGLE(80, "Angle"), + SUB_PICTURE(81, "Sub Picture"), + VIDEO_ON_DEMAND(82, "Video On Demand"), + ELECTRONIC_PROGRAM_GUIDE(83, "Electronic Program Guide"), + TIMER_PROGRAMMING(84, "Timer Programming"), + INITIAL_CONFIGURATION(85, "Initial Configuration"), + SELECT_BROADCAST_TYPE(86, "Select Broadcast Type"), + SELECT_SOUND_PRESENTATION(87, "Select Sound Presentation"), + PLAY_FUNCTION(96, "Play Function"), + PAUSE_PLAY_FUNCTION(97, "Pause Play Function"), + RECORD_FUNCTION(98, "Record Function"), + PAUSE_RECORD_FUNCTION(99, "Pause Record Function"), + STOP_FUNCTION(100, "Stop Function"), + MUTE_FUNCTION(101, "Mute Function"), + RESTORE_VOLUME_FUNCTION(102, "Restore Volume Function"), + TUNE_FUNCTION(103, "Tune Function"), + SELECT_MEDIA_FUNCTION(104, "Select Media Function"), + SELECT_AV_INPUT_FUNCTION(105, "Select Av Input Function"), + SELECT_AUDIO_INPUT_FUNCTION(106, "Select Audio Input Function"), + POWER_TOGGLE_FUNCTION(107, "Power Toggle Function"), + POWER_OFF_FUNCTION(108, "Power Off Function"), + POWER_ON_FUNCTION(109, "Power On Function"), + F1BLUE(113, "F 1 Blue"), + F2RED(114, "F 2 Red"), + F3GREEN(115, "F 3 Green"), + F4YELLOW(116, "F 4 Yellow"), + F5(117, "F 5"), + DATA(118, "Data"); + + public final Integer value; + public final String label; + + private CecKeyCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Supports UP, DOWN, LEFT, RIGHT, SELECT, BACK, EXIT, MENU + */ + public boolean navigationKeyCodes; + /** + * + * Supports CEC keys 0x0A (Settings) and 0x09 (Home) + */ + public boolean locationKeys; + /** + * + * Supports numeric input 0..9 + */ + public boolean numberKeys; + + public FeatureMap(boolean navigationKeyCodes, boolean locationKeys, boolean numberKeys) { + this.navigationKeyCodes = navigationKeyCodes; + this.locationKeys = locationKeys; + this.numberKeys = numberKeys; + } + } + + public KeypadInputCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1289, "KeypadInput"); + } + + protected KeypadInputCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this shall process a keycode as input to the media endpoint. + * If a device has multiple media endpoints implementing this cluster, such as a casting video player endpoint with + * one or more content app endpoints, then only the endpoint receiving the command shall process the keycode as + * input. In other words, a specific content app endpoint shall NOT process a keycode received by a different + * content app endpoint. + * If a second SendKey request with the same KeyCode value is received within 200 ms, then the endpoint will + * consider the first key press to be a press and hold. When such a repeat KeyCode value is not received within 200 + * ms, then the endpoint will consider the last key press to be a release. + */ + public static ClusterCommand sendKey(CecKeyCodeEnum keyCode) { + Map map = new LinkedHashMap<>(); + if (keyCode != null) { + map.put("keyCode", keyCode); + } + return new ClusterCommand("sendKey", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LabelCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LabelCluster.java new file mode 100644 index 00000000000..459f8e273c4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LabelCluster.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Label + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class LabelCluster extends BaseCluster { + + public static final String CLUSTER_NAME = "Label"; + public static final String CLUSTER_PREFIX = "label"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_LABEL_LIST = "labelList"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This is a list of string tuples. Each entry is a LabelStruct. + */ + public List labelList; // 0 list + // Structs + + /** + * This is a string tuple with strings that are user defined. + */ + public class LabelStruct { + /** + * The Label or Value semantic is not defined here. Label examples: "room", "zone", + * "group", "direction". + */ + public String label; // string + /** + * The Label or Value semantic is not defined here. The Value is a discriminator for a Label that may have + * multiple instances. Label:Value examples: "room":"bedroom 2", + * "orientation":"North", "floor":"2", + * "direction":"up" + */ + public String value; // string + + public LabelStruct(String label, String value) { + this.label = label; + this.value = value; + } + } + + protected LabelCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "labelList : " + labelList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryDryerControlsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryDryerControlsCluster.java new file mode 100644 index 00000000000..f6f15b765d0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryDryerControlsCluster.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * LaundryDryerControls + * + * @author Dan Cunningham - Initial contribution + */ +public class LaundryDryerControlsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x004A; + public static final String CLUSTER_NAME = "LaundryDryerControls"; + public static final String CLUSTER_PREFIX = "laundryDryerControls"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_SUPPORTED_DRYNESS_LEVELS = "supportedDrynessLevels"; + public static final String ATTRIBUTE_SELECTED_DRYNESS_LEVEL = "selectedDrynessLevel"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the list of supported dryness levels available to the appliance in the currently selected mode. The + * dryness level values are determined by the manufacturer. At least one dryness level value shall be provided in + * the SupportedDrynessLevels list. The list of dryness levels may change depending on the currently-selected + * Laundry Dryer mode. + */ + public List supportedDrynessLevels; // 0 list R V + /** + * Indicates the currently-selected dryness level and it shall be the index into the SupportedDrynessLevels list of + * the selected dryness level. + * If an attempt is made to write this attribute with a value other than null or a value contained in + * SupportedDrynessLevels, a CONSTRAINT_ERROR response shall be sent as the response. If an attempt is made to write + * this attribute while the device is not in a state that supports modifying the dryness level, an INVALID_IN_STATE + * error shall be sent as the response. A value of null shall indicate that there will be no dryness level setting + * for the current mode. + */ + public DrynessLevelEnum selectedDrynessLevel; // 1 DrynessLevelEnum RW VO + + // Enums + /** + * This enum provides a representation of the level of dryness that will be used while drying in a selected mode. + * It is up to the device manufacturer to determine the mapping between the enum values and the corresponding + * temperature level. + */ + public enum DrynessLevelEnum implements MatterEnum { + LOW(0, "Low"), + NORMAL(1, "Normal"), + EXTRA(2, "Extra"), + MAX(3, "Max"); + + public final Integer value; + public final String label; + + private DrynessLevelEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public LaundryDryerControlsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 74, "LaundryDryerControls"); + } + + protected LaundryDryerControlsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "supportedDrynessLevels : " + supportedDrynessLevels + "\n"; + str += "selectedDrynessLevel : " + selectedDrynessLevel + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryWasherControlsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryWasherControlsCluster.java new file mode 100644 index 00000000000..09f0f704ebe --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryWasherControlsCluster.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * LaundryWasherControls + * + * @author Dan Cunningham - Initial contribution + */ +public class LaundryWasherControlsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0053; + public static final String CLUSTER_NAME = "LaundryWasherControls"; + public static final String CLUSTER_PREFIX = "laundryWasherControls"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_SPIN_SPEEDS = "spinSpeeds"; + public static final String ATTRIBUTE_SPIN_SPEED_CURRENT = "spinSpeedCurrent"; + public static final String ATTRIBUTE_NUMBER_OF_RINSES = "numberOfRinses"; + public static final String ATTRIBUTE_SUPPORTED_RINSES = "supportedRinses"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the list of spin speeds available to the appliance in the currently selected mode. The spin speed + * values are determined by the manufacturer. At least one spin speed value shall be provided in the SpinSpeeds + * list. The list of spin speeds may change depending on the currently selected Laundry Washer mode. For example, + * Quick mode might have a completely different list of SpinSpeeds than Delicates mode. + */ + public List spinSpeeds; // 0 list R V + /** + * Indicates the currently selected spin speed. It is the index into the SpinSpeeds list of the selected spin speed, + * as such, this attribute can be an integer between 0 and the number of entries in SpinSpeeds - 1. If a value is + * received that is outside of the defined constraints, a CONSTRAINT_ERROR shall be sent as the response. If a value + * is attempted to be written that doesn’t match a valid index (e.g. an index of 5 when the list has 4 values), a + * CONSTRAINT_ERROR shall be sent as the response. If null is written to this attribute, there will be no spin speed + * for the + * selected cycle. If the value is null, there will be no spin speed on the current mode. + */ + public Integer spinSpeedCurrent; // 1 uint8 RW VO + /** + * Indicates how many times a rinse cycle shall be performed on a device for the current mode of operation. A value + * of None shall indicate that no rinse cycle will be performed. This value may be set by the client to adjust the + * number of rinses that are performed for the current mode of operation. If the device is not in a compatible state + * to accept the provided value, an INVALID_IN_STATE error shall be sent as the response. + */ + public NumberOfRinsesEnum numberOfRinses; // 2 NumberOfRinsesEnum RW VO + /** + * Indicates the amount of rinses allowed for a specific mode. Each entry shall indicate a NumberOfRinsesEnum value + * that is possible in the selected mode on the device. The value of this attribute may change at runtime based on + * the currently selected mode. Each entry shall be distinct. + */ + public List supportedRinses; // 3 list R V + + // Enums + /** + * The NumberOfRinsesEnum provides a representation of the number of rinses that will be performed for a selected + * mode. NumberOfRinsesEnum is derived from enum8. It is up to the device manufacturer to determine the mapping + * between the enum values and the corresponding numbers of rinses. + */ + public enum NumberOfRinsesEnum implements MatterEnum { + NONE(0, "None"), + NORMAL(1, "Normal"), + EXTRA(2, "Extra"), + MAX(3, "Max"); + + public final Integer value; + public final String label; + + private NumberOfRinsesEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature indicates multiple spin speeds are supported in at least one supported mode. Note that some + * modes may not support multiple spin speeds even if this feature is supported. + */ + public boolean spin; + /** + * + * This feature indicates multiple rinse cycles are supported in at least one supported mode. Note that some + * modes may not support selection of the number of rinse cycles even if this feature is supported. + */ + public boolean rinse; + + public FeatureMap(boolean spin, boolean rinse) { + this.spin = spin; + this.rinse = rinse; + } + } + + public LaundryWasherControlsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 83, "LaundryWasherControls"); + } + + protected LaundryWasherControlsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "spinSpeeds : " + spinSpeeds + "\n"; + str += "spinSpeedCurrent : " + spinSpeedCurrent + "\n"; + str += "numberOfRinses : " + numberOfRinses + "\n"; + str += "supportedRinses : " + supportedRinses + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryWasherModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryWasherModeCluster.java new file mode 100644 index 00000000000..6bc7d109e28 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LaundryWasherModeCluster.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * LaundryWasherMode + * + * @author Dan Cunningham - Initial contribution + */ +public class LaundryWasherModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x0051; + public static final String CLUSTER_NAME = "LaundryWasherMode"; + public static final String CLUSTER_PREFIX = "laundryWasherMode"; + + // Structs + /** + * The table below lists the changes relative to the Mode Base cluster for the fields of the ModeOptionStruct type. + * A blank field indicates no change. + */ + public class ModeOptionStruct { + public String label; // + public String mode; // + public String modeTags; // + + public ModeOptionStruct(String label, String mode, String modeTags) { + this.label = label; + this.mode = mode; + this.modeTags = modeTags; + } + } + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + NORMAL(16384, "Normal"), + DELICATE(16385, "Delicate"), + HEAVY(16386, "Heavy"), + WHITES(16387, "Whites"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public LaundryWasherModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 81, "LaundryWasherMode"); + } + + protected LaundryWasherModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LevelControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LevelControlCluster.java new file mode 100644 index 00000000000..45de18324d3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LevelControlCluster.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * LevelControl + * + * @author Dan Cunningham - Initial contribution + */ +public class LevelControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0008; + public static final String CLUSTER_NAME = "LevelControl"; + public static final String CLUSTER_PREFIX = "levelControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CURRENT_LEVEL = "currentLevel"; + public static final String ATTRIBUTE_REMAINING_TIME = "remainingTime"; + public static final String ATTRIBUTE_MIN_LEVEL = "minLevel"; + public static final String ATTRIBUTE_MAX_LEVEL = "maxLevel"; + public static final String ATTRIBUTE_CURRENT_FREQUENCY = "currentFrequency"; + public static final String ATTRIBUTE_MIN_FREQUENCY = "minFrequency"; + public static final String ATTRIBUTE_MAX_FREQUENCY = "maxFrequency"; + public static final String ATTRIBUTE_ON_OFF_TRANSITION_TIME = "onOffTransitionTime"; + public static final String ATTRIBUTE_ON_LEVEL = "onLevel"; + public static final String ATTRIBUTE_ON_TRANSITION_TIME = "onTransitionTime"; + public static final String ATTRIBUTE_OFF_TRANSITION_TIME = "offTransitionTime"; + public static final String ATTRIBUTE_DEFAULT_MOVE_RATE = "defaultMoveRate"; + public static final String ATTRIBUTE_OPTIONS = "options"; + public static final String ATTRIBUTE_START_UP_CURRENT_LEVEL = "startUpCurrentLevel"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the current level of this device. The meaning of 'level' is device dependent. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second, or + * • At the end of the movement/transition, or + * • When it changes from null to any other value and vice versa. + */ + public Integer currentLevel; // 0 uint8 R V + /** + * Indicates the time remaining until the current command is complete - it is specified in 1/10ths of a second. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • When it changes from 0 to any value higher than 10, or + * • When it changes, with a delta larger than 10, caused by the invoke of a command, or + * • When it changes to 0. + * For commands with a transition time or changes to the transition time less than 1 second, changes to this + * attribute shall NOT be reported. + * As this attribute is not being reported during a regular countdown, clients SHOULD NOT rely on the reporting of + * this attribute in order to keep track of the remaining duration. + */ + public Integer remainingTime; // 1 uint16 R V + /** + * Indicates the minimum value of CurrentLevel that is capable of being assigned. + */ + public Integer minLevel; // 2 uint8 R V + /** + * Indicates the maximum value of CurrentLevel that is capable of being assigned. + */ + public Integer maxLevel; // 3 uint8 R V + /** + * This attribute shall indicate the frequency at which the device is at CurrentLevel. A CurrentFrequency of 0 is + * unknown. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once per second, or + * • At the start of the movement/transition, or + * • At the end of the movement/transition. + */ + public Integer currentFrequency; // 4 uint16 R V + /** + * Indicates the minimum value of CurrentFrequency that is capable of being assigned. MinFrequency shall be less + * than or equal to MaxFrequency. A value of 0 indicates undefined. + */ + public Integer minFrequency; // 5 uint16 R V + /** + * Indicates the maximum value of CurrentFrequency that is capable of being assigned. MaxFrequency shall be greater + * than or equal to MinFrequency. A value of 0 indicates undefined. + */ + public Integer maxFrequency; // 6 uint16 R V + /** + * Indicates the time taken to move to or from the target level when On or Off commands are received by an On/Off + * cluster on the same endpoint. It is specified in 1/10ths of a second. + * The actual time taken SHOULD be as close to OnOffTransitionTime as the device is able. Please note that if the + * device is not able to move at a variable rate, the OnOffTransitionTime attribute SHOULD NOT be implemented. + */ + public Integer onOffTransitionTime; // 16 uint16 RW VO + /** + * Indicates the value that the CurrentLevel attribute is set to when the OnOff attribute of an On/Off cluster on + * the same endpoint is set to TRUE, as a result of processing an On/Off cluster command. If the OnLevel attribute + * is not implemented, or is set to the null value, it has no effect. For more details see Effect of On/Off Commands + * on the CurrentLevel attribute. + * OnLevel represents a mandatory field that was previously not present or optional. Implementers should be aware + * that older devices may not implement it. + */ + public Integer onLevel; // 17 uint8 RW VO + /** + * Indicates the time taken to move the current level from the minimum level to the maximum level when an On command + * is received by an On/Off cluster on the same endpoint. It is specified in 1/10ths of a second. If this attribute + * is not implemented, or contains a null value, the OnOffTransitionTime shall be used instead. + */ + public Integer onTransitionTime; // 18 uint16 RW VO + /** + * Indicates the time taken to move the current level from the maximum level to the minimum level when an Off + * command is received by an On/Off cluster on the same endpoint. It is specified in 1/10ths of a second. If this + * attribute is not implemented, or contains a null value, the OnOffTransitionTime shall be used instead. + */ + public Integer offTransitionTime; // 19 uint16 RW VO + /** + * Indicates the movement rate, in units per second, when a Move command is received with a null value Rate + * parameter. + */ + public Integer defaultMoveRate; // 20 uint8 RW VO + /** + * Indicates the selected options of the device. + * The Options attribute is a bitmap that determines the default behavior of some cluster commands. Each command + * that is dependent on the Options attribute shall first construct a temporary Options bitmap that is in effect + * during the command processing. The temporary Options bitmap has the same format and meaning as the Options + * attribute, but includes any bits that may be overridden by command fields. + * This attribute is meant to be changed only during commissioning. + * Command execution shall NOT continue beyond the Options processing if all of these criteria are true: + * • The command is one of the ‘without On/Off’ commands: Move, Move to Level, Step, or Stop. + * • The On/Off cluster exists on the same endpoint as this cluster. + * • The OnOff attribute of the On/Off cluster, on this endpoint, is FALSE. + * • The value of the ExecuteIfOff bit is 0. + */ + public OptionsBitmap options; // 15 OptionsBitmap RW VO + /** + * Indicates the desired startup level for a device when it is supplied with power and this level shall be reflected + * in the CurrentLevel attribute. The values of the StartUpCurrentLevel attribute are listed below: + * This behavior does not apply to reboots associated with OTA. After an OTA restart, the CurrentLevel attribute + * shall return to its value prior to the restart. + */ + public Integer startUpCurrentLevel; // 16384 uint8 RW VM + + // Enums + public enum MoveModeEnum implements MatterEnum { + UP(0, "Up"), + DOWN(1, "Down"); + + public final Integer value; + public final String label; + + private MoveModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StepModeEnum implements MatterEnum { + UP(0, "Up"), + DOWN(1, "Down"); + + public final Integer value; + public final String label; + + private StepModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class OptionsBitmap { + /** + * Dependency on On/Off cluster + * This bit indicates if this cluster has a dependency with the On/Off cluster. + */ + public boolean executeIfOff; + /** + * Dependency on Color Control cluster + * This bit indicates if this cluster has a dependency with the Color Control cluster. + */ + public boolean coupleColorTempToLevel; + + public OptionsBitmap(boolean executeIfOff, boolean coupleColorTempToLevel) { + this.executeIfOff = executeIfOff; + this.coupleColorTempToLevel = coupleColorTempToLevel; + } + } + + public static class FeatureMap { + /** + * + * Dependency with the On/Off cluster + */ + public boolean onOff; + /** + * + * This feature supports an interface for controlling the level of a light source. For the CurrentLevel + * attribute: + * A value of 0x00 shall NOT be used. + * A value of 0x01 shall indicate the minimum level that can be attained on a device. A value of 0xFE shall + * indicate the maximum level that can be attained on a device. A value of null shall represent an undefined + * value. + * All other values are application specific gradations from the minimum to the maximum level. + */ + public boolean lighting; + /** + * + * Supports frequency attributes and behavior. + */ + public boolean frequency; + + public FeatureMap(boolean onOff, boolean lighting, boolean frequency) { + this.onOff = onOff; + this.lighting = lighting; + this.frequency = frequency; + } + } + + public LevelControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 8, "LevelControl"); + } + + protected LevelControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand moveToLevel(Integer level, Integer transitionTime, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (level != null) { + map.put("level", level); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToLevel", map); + } + + public static ClusterCommand move(MoveModeEnum moveMode, Integer rate, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (moveMode != null) { + map.put("moveMode", moveMode); + } + if (rate != null) { + map.put("rate", rate); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("move", map); + } + + public static ClusterCommand step(StepModeEnum stepMode, Integer stepSize, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepMode != null) { + map.put("stepMode", stepMode); + } + if (stepSize != null) { + map.put("stepSize", stepSize); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("step", map); + } + + public static ClusterCommand stop(OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stop", map); + } + + public static ClusterCommand moveToLevelWithOnOff(Integer level, Integer transitionTime, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (level != null) { + map.put("level", level); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveToLevelWithOnOff", map); + } + + public static ClusterCommand moveWithOnOff(MoveModeEnum moveMode, Integer rate, OptionsBitmap optionsMask, + OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (moveMode != null) { + map.put("moveMode", moveMode); + } + if (rate != null) { + map.put("rate", rate); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("moveWithOnOff", map); + } + + public static ClusterCommand stepWithOnOff(StepModeEnum stepMode, Integer stepSize, Integer transitionTime, + OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (stepMode != null) { + map.put("stepMode", stepMode); + } + if (stepSize != null) { + map.put("stepSize", stepSize); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stepWithOnOff", map); + } + + public static ClusterCommand stopWithOnOff(OptionsBitmap optionsMask, OptionsBitmap optionsOverride) { + Map map = new LinkedHashMap<>(); + if (optionsMask != null) { + map.put("optionsMask", optionsMask); + } + if (optionsOverride != null) { + map.put("optionsOverride", optionsOverride); + } + return new ClusterCommand("stopWithOnOff", map); + } + + public static ClusterCommand moveToClosestFrequency(Integer frequency) { + Map map = new LinkedHashMap<>(); + if (frequency != null) { + map.put("frequency", frequency); + } + return new ClusterCommand("moveToClosestFrequency", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "currentLevel : " + currentLevel + "\n"; + str += "remainingTime : " + remainingTime + "\n"; + str += "minLevel : " + minLevel + "\n"; + str += "maxLevel : " + maxLevel + "\n"; + str += "currentFrequency : " + currentFrequency + "\n"; + str += "minFrequency : " + minFrequency + "\n"; + str += "maxFrequency : " + maxFrequency + "\n"; + str += "onOffTransitionTime : " + onOffTransitionTime + "\n"; + str += "onLevel : " + onLevel + "\n"; + str += "onTransitionTime : " + onTransitionTime + "\n"; + str += "offTransitionTime : " + offTransitionTime + "\n"; + str += "defaultMoveRate : " + defaultMoveRate + "\n"; + str += "options : " + options + "\n"; + str += "startUpCurrentLevel : " + startUpCurrentLevel + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LocalizationConfigurationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LocalizationConfigurationCluster.java new file mode 100644 index 00000000000..f760a45c971 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LocalizationConfigurationCluster.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * LocalizationConfiguration + * + * @author Dan Cunningham - Initial contribution + */ +public class LocalizationConfigurationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x002B; + public static final String CLUSTER_NAME = "LocalizationConfiguration"; + public static final String CLUSTER_PREFIX = "localizationConfiguration"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_ACTIVE_LOCALE = "activeLocale"; + public static final String ATTRIBUTE_SUPPORTED_LOCALES = "supportedLocales"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * The ActiveLocale attribute shall represent the locale that the Node is currently configured to use when conveying + * information. The ActiveLocale attribute shall be a Language Tag as defined by BCP47. The ActiveLocale attribute + * shall have a default value assigned by the Vendor and shall be a value contained within the SupportedLocales + * attribute. + * An attempt to write a value to ActiveLocale that is not present in SupportedLocales shall result in a + * CONSTRAINT_ERROR error. + */ + public String activeLocale; // 0 string RW VM + /** + * The SupportedLocales attribute shall represent a list of locale strings that are valid values for the + * ActiveLocale attribute. The list shall NOT contain any duplicate entries. The ordering of items within the list + * SHOULD NOT express any meaning. + */ + public List supportedLocales; // 1 list R V + + public LocalizationConfigurationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 43, "LocalizationConfiguration"); + } + + protected LocalizationConfigurationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "activeLocale : " + activeLocale + "\n"; + str += "supportedLocales : " + supportedLocales + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LowPowerCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LowPowerCluster.java new file mode 100644 index 00000000000..0145d4453d6 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/LowPowerCluster.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * LowPower + * + * @author Dan Cunningham - Initial contribution + */ +public class LowPowerCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0508; + public static final String CLUSTER_NAME = "LowPower"; + public static final String CLUSTER_PREFIX = "lowPower"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + + public LowPowerCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1288, "LowPower"); + } + + protected LowPowerCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall put the device into low power mode. + */ + public static ClusterCommand sleep() { + return new ClusterCommand("sleep"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MediaInputCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MediaInputCluster.java new file mode 100644 index 00000000000..9e3585dce84 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MediaInputCluster.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * MediaInput + * + * @author Dan Cunningham - Initial contribution + */ +public class MediaInputCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0507; + public static final String CLUSTER_NAME = "MediaInput"; + public static final String CLUSTER_PREFIX = "mediaInput"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_INPUT_LIST = "inputList"; + public static final String ATTRIBUTE_CURRENT_INPUT = "currentInput"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall provide a list of the media inputs supported by the device. + */ + public List inputList; // 0 list R V + /** + * This attribute shall contain the value of the index field of the currently selected InputInfoStruct. + */ + public Integer currentInput; // 1 uint8 R V + // Structs + + /** + * This contains information about an input. + */ + public class InputInfoStruct { + /** + * This field shall indicate the unique index into the list of Inputs. + */ + public Integer index; // uint8 + /** + * ### This field shall indicate the type of input + */ + public InputTypeEnum inputType; // InputTypeEnum + /** + * This field shall indicate the input name, such as “HDMI 1”. This field may be blank, but SHOULD be provided + * when known. + */ + public String name; // string + /** + * This field shall indicate the user editable input description, such as “Living room Playstation”. This field + * may be blank, but SHOULD be provided when known. + */ + public String description; // string + + public InputInfoStruct(Integer index, InputTypeEnum inputType, String name, String description) { + this.index = index; + this.inputType = inputType; + this.name = name; + this.description = description; + } + } + + // Enums + public enum InputTypeEnum implements MatterEnum { + INTERNAL(0, "Internal"), + AUX(1, "Aux"), + COAX(2, "Coax"), + COMPOSITE(3, "Composite"), + HDMI(4, "Hdmi"), + INPUT(5, "Input"), + LINE(6, "Line"), + OPTICAL(7, "Optical"), + VIDEO(8, "Video"), + SCART(9, "Scart"), + USB(10, "Usb"), + OTHER(11, "Other"); + + public final Integer value; + public final String label; + + private InputTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Supports updates to the input names + */ + public boolean nameUpdates; + + public FeatureMap(boolean nameUpdates) { + this.nameUpdates = nameUpdates; + } + } + + public MediaInputCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1287, "MediaInput"); + } + + protected MediaInputCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this command shall change the media input on the device to the input at a specific index in the + * Input List. + */ + public static ClusterCommand selectInput(Integer index) { + Map map = new LinkedHashMap<>(); + if (index != null) { + map.put("index", index); + } + return new ClusterCommand("selectInput", map); + } + + /** + * Upon receipt, this command shall display the active status of the input list on screen. + */ + public static ClusterCommand showInputStatus() { + return new ClusterCommand("showInputStatus"); + } + + /** + * Upon receipt, this command shall hide the input list from the screen. + */ + public static ClusterCommand hideInputStatus() { + return new ClusterCommand("hideInputStatus"); + } + + /** + * Upon receipt, this command shall rename the input at a specific index in the Input List. Updates to the input + * name shall appear in the device’s settings menus. + */ + public static ClusterCommand renameInput(Integer index, String name) { + Map map = new LinkedHashMap<>(); + if (index != null) { + map.put("index", index); + } + if (name != null) { + map.put("name", name); + } + return new ClusterCommand("renameInput", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "inputList : " + inputList + "\n"; + str += "currentInput : " + currentInput + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MediaPlaybackCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MediaPlaybackCluster.java new file mode 100644 index 00000000000..92846b571d3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MediaPlaybackCluster.java @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * MediaPlayback + * + * @author Dan Cunningham - Initial contribution + */ +public class MediaPlaybackCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0506; + public static final String CLUSTER_NAME = "MediaPlayback"; + public static final String CLUSTER_PREFIX = "mediaPlayback"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CURRENT_STATE = "currentState"; + public static final String ATTRIBUTE_START_TIME = "startTime"; + public static final String ATTRIBUTE_DURATION = "duration"; + public static final String ATTRIBUTE_SAMPLED_POSITION = "sampledPosition"; + public static final String ATTRIBUTE_PLAYBACK_SPEED = "playbackSpeed"; + public static final String ATTRIBUTE_SEEK_RANGE_END = "seekRangeEnd"; + public static final String ATTRIBUTE_SEEK_RANGE_START = "seekRangeStart"; + public static final String ATTRIBUTE_ACTIVE_AUDIO_TRACK = "activeAudioTrack"; + public static final String ATTRIBUTE_AVAILABLE_AUDIO_TRACKS = "availableAudioTracks"; + public static final String ATTRIBUTE_ACTIVE_TEXT_TRACK = "activeTextTrack"; + public static final String ATTRIBUTE_AVAILABLE_TEXT_TRACKS = "availableTextTracks"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the current playback state of media. + * During fast-forward, rewind, and other seek operations; this attribute shall be set to PLAYING. + */ + public PlaybackStateEnum currentState; // 0 PlaybackStateEnum R V + /** + * Indicates the start time of the media, in case the media has a fixed start time (for example, live stream or + * television broadcast), or null when start time does not apply to the current media (for example, + * video-on-demand). This time is a UTC time. The client needs to handle conversion to local time, as required, + * taking in account time zone and possible local DST offset. + */ + public BigInteger startTime; // 1 epoch-us R V + /** + * Indicates the duration, in milliseconds, of the current media being played back or null when duration is not + * applicable (for example, in live streaming content with no known duration). This attribute shall never be 0. + */ + public BigInteger duration; // 2 uint64 R V + /** + * Indicates the position of playback (Position field) at the time (UpdateAt field) specified in the attribute. The + * client may use the SampledPosition attribute to compute the current position within the media stream based on the + * PlaybackSpeed, PlaybackPositionStruct.UpdatedAt and PlaybackPositionStruct.Position fields. To enable this, the + * SampledPosition attribute shall be updated whenever a change in either the playback speed or the playback + * position is triggered outside the normal playback of the media. The events which may cause this to happen + * include: + * • Starting or resumption of playback + * • Seeking + * • Skipping forward or backward + * • Fast-forwarding or rewinding + * • Updating of playback speed as a result of explicit request, or as a result of buffering events + */ + public PlaybackPositionStruct sampledPosition; // 3 PlaybackPositionStruct R V + /** + * Indicates the speed at which the current media is being played. The new PlaybackSpeed shall be reflected in this + * attribute whenever any of the following occurs: + * • Starting of playback + * • Resuming of playback + * • Fast-forwarding + * • Rewinding + * The PlaybackSpeed shall reflect the ratio of time elapsed in the media to the actual time taken for the playback + * assuming no changes to media playback (for example buffering events or requests to pause/rewind/forward). + * • A value for PlaybackSpeed of 1 shall indicate normal playback where, for example, playback for 1 second causes + * the media to advance by 1 second within the duration of the media. + * • A value for PlaybackSpeed which is greater than 0 shall indicate that as playback is happening the media is + * currently advancing in time within the duration of the media. + * • A value for PlaybackSpeed which is less than 0 shall indicate that as playback is happening the media is + * currently going back in time within the duration of the media. + * • A value for PlaybackSpeed of 0 shall indicate that the media is currently not playing back. When the + * CurrentState attribute has the value of PAUSED, NOT_PLAYING or BUFFERING, the PlaybackSpeed shall be set to 0 to + * reflect that the media is not playing. + * Following examples illustrate the PlaybackSpeed attribute values in various conditions. + */ + public Float playbackSpeed; // 4 single R V + /** + * Indicates the furthest forward valid position to which a client may seek forward, in milliseconds from the start + * of the media. When the media has an associated StartTime, a value of null shall indicate that a seek forward is + * valid only until the current time within the media, using a position computed from the difference between the + * current time offset and StartTime, in milliseconds from start of the media, truncating fractional milliseconds + * towards 0. A value of Nas when StartTime is not specified shall indicate that seeking forward is not allowed. + */ + public BigInteger seekRangeEnd; // 5 uint64 R V + /** + * Indicates the earliest valid position to which a client may seek back, in milliseconds from start of the media. A + * value of Nas shall indicate that seeking backwards is not allowed. + */ + public BigInteger seekRangeStart; // 6 uint64 R V + /** + * ActiveTrack refers to the Audio track currently set and being used for the streaming media. A value of null shall + * indicate that no Audio Track corresponding to the current media is currently being played. + */ + public TrackStruct activeAudioTrack; // 7 TrackStruct R V + /** + * AvailableAudioTracks refers to the list of Audio tracks available for the current title being played. A value of + * null shall indicate that no Audio Tracks corresponding to the current media are selectable by the client. + */ + public List availableAudioTracks; // 8 list R V + /** + * ActiveTrack refers to the Text track currently set and being used for the streaming media. This can be nil. A + * value of null shall indicate that no Text Track corresponding to the current media is currently being displayed. + */ + public TrackStruct activeTextTrack; // 9 TrackStruct R V + /** + * AvailableTextTracks refers to the list of Text tracks available for the current title being played. This can be + * an empty list. A value of null shall indicate that no Text Tracks corresponding to the current media are + * selectable by the client. + */ + public List availableTextTracks; // 10 list R V + // Structs + + /** + * If supported, this event shall be generated when there is a change in any of the supported attributes of the + * Media Playback cluster. + */ + public class StateChanged { + /** + * This field shall indicate the updated playback state as defined by the CurrentState attribute, and has the + * same constraint as that attribute. + */ + public PlaybackStateEnum currentState; // PlaybackStateEnum + /** + * This field shall indicate the updated start time as defined by the StartTime attribute, and has the same + * constraint as that attribute. + */ + public BigInteger startTime; // epoch-us + /** + * This field shall indicate the updated duration as defined by the Duration attribute, and has the same + * constraint as that attribute. + */ + public BigInteger duration; // uint64 + /** + * This field shall indicate the updated position of playback as defined by the SampledPosition attribute, and + * has the same constraint as that attribute. + */ + public PlaybackPositionStruct sampledPosition; // PlaybackPositionStruct + /** + * This field shall indicate the updated speed at which the current media is being played as defined by the + * PlaybackSpeed attribute, and has the same constraint as that attribute. + */ + public Float playbackSpeed; // single + /** + * This field shall indicate the updated start of the seek range end as defined by the SeekRangeEnd attribute, + * and has the same constraint as that attribute. + */ + public BigInteger seekRangeEnd; // uint64 + /** + * This field shall indicate the updated start of the seek range start as defined by the SeekRangeStart + * attribute, and has the same constraint as that attribute. + */ + public BigInteger seekRangeStart; // uint64 + /** + * This field shall indicate Optional app-specific data. + */ + public OctetString data; // octstr + /** + * This field shall indicate whether audio is unmuted by the player due to a FF or REW command. This field is + * only meaningful when the PlaybackSpeed is present and not equal to 0 (paused) or 1 (normal playback). + * Typically the value will be false (muted), however, some players will play audio during certain fast forward + * and rewind speeds, and in these cases, the value will be true (not muted). + * A value of true does not guarantee that audio can be heard by the user since the speaker may be muted, turned + * down to a low level and/or unplugged. + */ + public Boolean audioAdvanceUnmuted; // bool + + public StateChanged(PlaybackStateEnum currentState, BigInteger startTime, BigInteger duration, + PlaybackPositionStruct sampledPosition, Float playbackSpeed, BigInteger seekRangeEnd, + BigInteger seekRangeStart, OctetString data, Boolean audioAdvanceUnmuted) { + this.currentState = currentState; + this.startTime = startTime; + this.duration = duration; + this.sampledPosition = sampledPosition; + this.playbackSpeed = playbackSpeed; + this.seekRangeEnd = seekRangeEnd; + this.seekRangeStart = seekRangeStart; + this.data = data; + this.audioAdvanceUnmuted = audioAdvanceUnmuted; + } + } + + /** + * This structure defines a playback position within a media stream being played. + */ + public class PlaybackPositionStruct { + /** + * This field shall indicate the time when the position was last updated. + */ + public BigInteger updatedAt; // epoch-us + /** + * This field shall indicate the associated discrete position within the media stream, in milliseconds from the + * beginning of the stream, being associated with the time indicated by the UpdatedAt field. The Position shall + * NOT be greater than the duration of the media if duration is specified. The Position shall NOT be greater + * than the time difference between current time and start time of the media when start time is specified. + * A value of null shall indicate that playback position is not applicable for the current state of the media + * playback (For example : Live media with no known duration and where seek is not supported). + */ + public BigInteger position; // uint64 + + public PlaybackPositionStruct(BigInteger updatedAt, BigInteger position) { + this.updatedAt = updatedAt; + this.position = position; + } + } + + /** + * This structure defines a uniquely identifiable Text Track or Audio Track. + */ + public class TrackStruct { + /** + * This field shall indicate the Identifier for the Track which is unique within the Track catalog. The Track + * catalog contains all the Text/Audio tracks corresponding to the main media content. + */ + public String id; // string + /** + * This field shall indicate the Attributes associated to the Track, like languageCode. + */ + public TrackAttributesStruct trackAttributes; // TrackAttributesStruct + + public TrackStruct(String id, TrackAttributesStruct trackAttributes) { + this.id = id; + this.trackAttributes = trackAttributes; + } + } + + /** + * This structure includes the attributes associated with a Text/Audio Track + */ + public class TrackAttributesStruct { + /** + * The value is a String containing one of the standard Tags for Identifying Languages RFC 5646, which + * identifies the primary language used in the Track. + */ + public String languageCode; // string + /** + * This is a list of enumerated CharacteristicEnum values that indicate a purpose, trait or feature associated + * with the Track. A value of null shall indicate that there are no Characteristics corresponding to the Track. + */ + public List characteristics; // list + /** + * The value is a String containing a user displayable name for the Track. A value of null shall indicate that + * there is no DisplayName corresponding to the Track. + */ + public String displayName; // string + + public TrackAttributesStruct(String languageCode, List characteristics, + String displayName) { + this.languageCode = languageCode; + this.characteristics = characteristics; + this.displayName = displayName; + } + } + + // Enums + public enum PlaybackStateEnum implements MatterEnum { + PLAYING(0, "Playing"), + PAUSED(1, "Paused"), + NOT_PLAYING(2, "Not Playing"), + BUFFERING(3, "Buffering"); + + public final Integer value; + public final String label; + + private PlaybackStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + INVALID_STATE_FOR_COMMAND(1, "Invalid State For Command"), + NOT_ALLOWED(2, "Not Allowed"), + NOT_ACTIVE(3, "Not Active"), + SPEED_OUT_OF_RANGE(4, "Speed Out Of Range"), + SEEK_OUT_OF_RANGE(5, "Seek Out Of Range"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum CharacteristicEnum implements MatterEnum { + FORCED_SUBTITLES(0, "Forced Subtitles"), + DESCRIBES_VIDEO(1, "Describes Video"), + EASY_TO_READ(2, "Easy To Read"), + FRAME_BASED(3, "Frame Based"), + MAIN_PROGRAM(4, "Main Program"), + ORIGINAL_CONTENT(5, "Original Content"), + VOICE_OVER_TRANSLATION(6, "Voice Over Translation"), + CAPTION(7, "Caption"), + SUBTITLE(8, "Subtitle"), + ALTERNATE(9, "Alternate"), + SUPPLEMENTARY(10, "Supplementary"), + COMMENTARY(11, "Commentary"), + DUBBED_TRANSLATION(12, "Dubbed Translation"), + DESCRIPTION(13, "Description"), + METADATA(14, "Metadata"), + ENHANCED_AUDIO_INTELLIGIBILITY(15, "Enhanced Audio Intelligibility"), + EMERGENCY(16, "Emergency"), + KARAOKE(17, "Karaoke"); + + public final Integer value; + public final String label; + + private CharacteristicEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature provides access to the time offset location within current playback media and allows for jumping + * to a specific location using time offsets. This enables clients to implement more advanced media seeking + * behavior in their user interface, for instance a "seek bar". + */ + public boolean advancedSeek; + /** + * + * This feature is for a device which supports variable speed playback on media that supports it. + */ + public boolean variableSpeed; + /** + * + * This feature is for a device or app that supports Text Tracks. + */ + public boolean textTracks; + /** + * + * This feature is for a device or app that supports Audio Tracks. + */ + public boolean audioTracks; + /** + * + * This feature is for a device or app that supports playing audio during fast and slow advance and rewind + * (e.g., while playback speed is not 1). A device that supports this feature may only support playing audio + * during certain speeds. + * A cluster implementing AA shall implement AS. + */ + public boolean audioAdvance; + + public FeatureMap(boolean advancedSeek, boolean variableSpeed, boolean textTracks, boolean audioTracks, + boolean audioAdvance) { + this.advancedSeek = advancedSeek; + this.variableSpeed = variableSpeed; + this.textTracks = textTracks; + this.audioTracks = audioTracks; + this.audioAdvance = audioAdvance; + } + } + + public MediaPlaybackCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1286, "MediaPlayback"); + } + + protected MediaPlaybackCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this shall play media. If content is currently in a FastForward or Rewind state. Play shall return + * media to normal playback speed. + */ + public static ClusterCommand play() { + return new ClusterCommand("play"); + } + + /** + * Upon receipt, this shall pause playback of the media. + */ + public static ClusterCommand pause() { + return new ClusterCommand("pause"); + } + + /** + * Upon receipt, this shall stop playback of the media. User-visible outcome is context-specific. This may navigate + * the user back to the location from where the media was originally launched. + */ + public static ClusterCommand stop() { + return new ClusterCommand("stop"); + } + + /** + * Upon receipt, this shall Start Over with the current media playback item. + */ + public static ClusterCommand startOver() { + return new ClusterCommand("startOver"); + } + + /** + * Upon receipt, this shall cause the handler to be invoked for "Previous". User experience is + * context-specific. This will often Go back to the previous media playback item. + */ + public static ClusterCommand previous() { + return new ClusterCommand("previous"); + } + + /** + * Upon receipt, this shall cause the handler to be invoked for "Next". User experience is + * context-specific. This will often Go forward to the next media playback item. + */ + public static ClusterCommand next() { + return new ClusterCommand("next"); + } + + /** + * Upon receipt, this shall start playback of the media backward in case the media is currently playing in the + * forward direction or is not playing. If the playback is already happening in the backwards direction receipt of + * this command shall increase the speed of the media playback backwards. + * Different "rewind" speeds may be reflected on the media playback device based upon the number of + * sequential calls to this function and the capability of the device. This is to avoid needing to define every + * speed (multiple fast, slow motion, etc). If the PlaybackSpeed attribute is supported it shall be updated to + * reflect the new speed of playback. If the playback speed cannot be changed for the media being played(for + * example, in live streaming content not supporting seek), the status of NOT_ALLOWED shall be returned. If the + * playback speed has reached the maximum supported speed for media playing backwards, the status of + * SPEED_OUT_OF_RANGE shall be returned. + */ + public static ClusterCommand rewind(Boolean audioAdvanceUnmuted) { + Map map = new LinkedHashMap<>(); + if (audioAdvanceUnmuted != null) { + map.put("audioAdvanceUnmuted", audioAdvanceUnmuted); + } + return new ClusterCommand("rewind", map); + } + + /** + * Upon receipt, this shall start playback of the media in the forward direction in case the media is currently + * playing in the backward direction or is not playing. If the playback is already happening in the forward + * direction receipt of this command shall increase the speed of the media playback. + * Different "fast-forward" speeds may be reflected on the media playback device based upon the number of + * sequential calls to this function and the capability of the device. This is to avoid needing to define every + * speed (multiple fast, slow motion, etc). If the PlaybackSpeed attribute is supported it shall be updated to + * reflect the new speed of playback. If the playback speed cannot be changed for the media being played(for + * example, in live streaming content not supporting seek), the status of NOT_ALLOWED shall be returned. If the + * playback speed has reached the maximum supported speed for media playing forward, the status of + * SPEED_OUT_OF_RANGE shall be returned. + */ + public static ClusterCommand fastForward(Boolean audioAdvanceUnmuted) { + Map map = new LinkedHashMap<>(); + if (audioAdvanceUnmuted != null) { + map.put("audioAdvanceUnmuted", audioAdvanceUnmuted); + } + return new ClusterCommand("fastForward", map); + } + + /** + * Upon receipt, this shall Skip forward in the media by the given number of milliseconds. + */ + public static ClusterCommand skipForward(BigInteger deltaPositionMilliseconds) { + Map map = new LinkedHashMap<>(); + if (deltaPositionMilliseconds != null) { + map.put("deltaPositionMilliseconds", deltaPositionMilliseconds); + } + return new ClusterCommand("skipForward", map); + } + + /** + * Upon receipt, this shall Skip backward in the media by the given number of milliseconds. + */ + public static ClusterCommand skipBackward(BigInteger deltaPositionMilliseconds) { + Map map = new LinkedHashMap<>(); + if (deltaPositionMilliseconds != null) { + map.put("deltaPositionMilliseconds", deltaPositionMilliseconds); + } + return new ClusterCommand("skipBackward", map); + } + + /** + * Upon receipt, this shall change the playback position in the media to the given position. + */ + public static ClusterCommand seek(BigInteger position) { + Map map = new LinkedHashMap<>(); + if (position != null) { + map.put("position", position); + } + return new ClusterCommand("seek", map); + } + + /** + * Upon receipt, the server shall set the active Audio Track to the one identified by the TrackID in the Track + * catalog for the streaming media. If the TrackID does not exist in the Track catalog, OR does not correspond to + * the streaming media OR no media is being streamed at the time of receipt of this command, the server will return + * an error status of INVALID_ARGUMENT. + */ + public static ClusterCommand activateAudioTrack(String trackId, Integer audioOutputIndex) { + Map map = new LinkedHashMap<>(); + if (trackId != null) { + map.put("trackId", trackId); + } + if (audioOutputIndex != null) { + map.put("audioOutputIndex", audioOutputIndex); + } + return new ClusterCommand("activateAudioTrack", map); + } + + /** + * Upon receipt, the server shall set the active Text Track to the one identified by the TrackID in the Track + * catalog for the streaming media. If the TrackID does not exist in the Track catalog, OR does not correspond to + * the streaming media OR no media is being streamed at the time of receipt of this command, the server shall return + * an error status of INVALID_ARGUMENT. + */ + public static ClusterCommand activateTextTrack(String trackId) { + Map map = new LinkedHashMap<>(); + if (trackId != null) { + map.put("trackId", trackId); + } + return new ClusterCommand("activateTextTrack", map); + } + + /** + * If a Text Track is active (i.e. being displayed), upon receipt of this command, the server shall stop displaying + * it. + */ + public static ClusterCommand deactivateTextTrack() { + return new ClusterCommand("deactivateTextTrack"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "currentState : " + currentState + "\n"; + str += "startTime : " + startTime + "\n"; + str += "duration : " + duration + "\n"; + str += "sampledPosition : " + sampledPosition + "\n"; + str += "playbackSpeed : " + playbackSpeed + "\n"; + str += "seekRangeEnd : " + seekRangeEnd + "\n"; + str += "seekRangeStart : " + seekRangeStart + "\n"; + str += "activeAudioTrack : " + activeAudioTrack + "\n"; + str += "availableAudioTracks : " + availableAudioTracks + "\n"; + str += "activeTextTrack : " + activeTextTrack + "\n"; + str += "availableTextTracks : " + availableTextTracks + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MicrowaveOvenControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MicrowaveOvenControlCluster.java new file mode 100644 index 00000000000..e4e0dc39853 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MicrowaveOvenControlCluster.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * MicrowaveOvenControl + * + * @author Dan Cunningham - Initial contribution + */ +public class MicrowaveOvenControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x005F; + public static final String CLUSTER_NAME = "MicrowaveOvenControl"; + public static final String CLUSTER_PREFIX = "microwaveOvenControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_COOK_TIME = "cookTime"; + public static final String ATTRIBUTE_MAX_COOK_TIME = "maxCookTime"; + public static final String ATTRIBUTE_POWER_SETTING = "powerSetting"; + public static final String ATTRIBUTE_MIN_POWER = "minPower"; + public static final String ATTRIBUTE_MAX_POWER = "maxPower"; + public static final String ATTRIBUTE_POWER_STEP = "powerStep"; + public static final String ATTRIBUTE_SUPPORTED_WATTS = "supportedWatts"; + public static final String ATTRIBUTE_SELECTED_WATT_INDEX = "selectedWattIndex"; + public static final String ATTRIBUTE_WATT_RATING = "wattRating"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the total cook time associated with the operation of the device. + * This attribute shall remain unchanged during the operation of the oven unless the value is changed via a command + * or out-of-band action. + */ + public Integer cookTime; // 0 elapsed-s R V + /** + * Indicates the maximum value to which the CookTime attribute can be set. + */ + public Integer maxCookTime; // 1 elapsed-s R V + /** + * Indicates the power level associated with the operation of the device. If the MinPower, MaxPower, and PowerStep + * attributes are not supported: + * • The minimum value of this attribute shall be 10, + * • The maximum value of this attribute shall be 100, + * • The value shall be in even multiples of 10, + * • The default value shall be 100. + * If the MinPower, MaxPower, and PowerStep attributes are supported: + * • The value of this attribute shall be between MinPower and MaxPower inclusive. + * • The value of this attribute shall be such that (PowerSetting - MinPower) % PowerStep == 0 + */ + public Integer powerSetting; // 2 uint8 R V + /** + * Indicates the minimum value to which the PowerSetting attribute that can be set on the server. + */ + public Integer minPower; // 3 uint8 R V + /** + * Indicates the maximum value to which the PowerSetting attribute that can be set on the server. + */ + public Integer maxPower; // 4 uint8 R V + /** + * Indicates the increment of power that can be set on the server. The value of this attribute shall be between 1 + * and MaxPower inclusive. + * The value of this attribute shall be such that (MaxPower - MinPower) % PowerStep == 0 + * For example, if MinPower is 1, MaxPower is 10, and PowerSetting can be set to any integer between MinPower and + * MaxPower, PowerStep would be set to 1. + */ + public Integer powerStep; // 5 uint8 R V + /** + * Indicates the list of power levels (in W) supported by the server. + */ + public List supportedWatts; // 6 list R V + /** + * Indicates the index into the list of SupportedWatts of the currently selected power setting. + * The index shall be a valid index into the SupportedWatts list. + */ + public Integer selectedWattIndex; // 7 uint8 R V + /** + * Indicates the rating, in Watts, of the microwave power of the oven. + * Supporting this attribute can assist clients in suggesting cooking settings for various foods and beverages. + */ + public Integer wattRating; // 8 uint16 R V + + // Bitmaps + public static class FeatureMap { + /** + * + * Power is specified as a unitless number or a percentage + */ + public boolean powerAsNumber; + /** + * + * Power is specified in Watts + */ + public boolean powerInWatts; + /** + * + * Supports the limit attributes used with the PWRNUM feature + */ + public boolean powerNumberLimits; + + public FeatureMap(boolean powerAsNumber, boolean powerInWatts, boolean powerNumberLimits) { + this.powerAsNumber = powerAsNumber; + this.powerInWatts = powerInWatts; + this.powerNumberLimits = powerNumberLimits; + } + } + + public MicrowaveOvenControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 95, "MicrowaveOvenControl"); + } + + protected MicrowaveOvenControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used to set the cooking parameters associated with the operation of the device. This command + * supports the following fields: + */ + public static ClusterCommand setCookingParameters(Integer cookMode, Integer cookTime, Integer powerSetting, + Integer wattSettingIndex, Boolean startAfterSetting) { + Map map = new LinkedHashMap<>(); + if (cookMode != null) { + map.put("cookMode", cookMode); + } + if (cookTime != null) { + map.put("cookTime", cookTime); + } + if (powerSetting != null) { + map.put("powerSetting", powerSetting); + } + if (wattSettingIndex != null) { + map.put("wattSettingIndex", wattSettingIndex); + } + if (startAfterSetting != null) { + map.put("startAfterSetting", startAfterSetting); + } + return new ClusterCommand("setCookingParameters", map); + } + + /** + * This command is used to add more time to the CookTime attribute of the server. This command supports these + * fields: + */ + public static ClusterCommand addMoreTime(Integer timeToAdd) { + Map map = new LinkedHashMap<>(); + if (timeToAdd != null) { + map.put("timeToAdd", timeToAdd); + } + return new ClusterCommand("addMoreTime", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "cookTime : " + cookTime + "\n"; + str += "maxCookTime : " + maxCookTime + "\n"; + str += "powerSetting : " + powerSetting + "\n"; + str += "minPower : " + minPower + "\n"; + str += "maxPower : " + maxPower + "\n"; + str += "powerStep : " + powerStep + "\n"; + str += "supportedWatts : " + supportedWatts + "\n"; + str += "selectedWattIndex : " + selectedWattIndex + "\n"; + str += "wattRating : " + wattRating + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MicrowaveOvenModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MicrowaveOvenModeCluster.java new file mode 100644 index 00000000000..b6787cd1a56 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/MicrowaveOvenModeCluster.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * MicrowaveOvenMode + * + * @author Dan Cunningham - Initial contribution + */ +public class MicrowaveOvenModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x005E; + public static final String CLUSTER_NAME = "MicrowaveOvenMode"; + public static final String CLUSTER_PREFIX = "microwaveOvenMode"; + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + NORMAL(16384, "Normal"), + DEFROST(16385, "Defrost"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public MicrowaveOvenModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 94, "MicrowaveOvenMode"); + } + + protected MicrowaveOvenModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ModeBaseCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ModeBaseCluster.java new file mode 100644 index 00000000000..8dae2898ee9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ModeBaseCluster.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ModeBase + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class ModeBaseCluster extends BaseCluster { + + public static final String CLUSTER_NAME = "ModeBase"; + public static final String CLUSTER_PREFIX = "modeBase"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_SUPPORTED_MODES = "supportedModes"; + public static final String ATTRIBUTE_CURRENT_MODE = "currentMode"; + public static final String ATTRIBUTE_START_UP_MODE = "startUpMode"; + public static final String ATTRIBUTE_ON_MODE = "onMode"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall contain the list of supported modes that may be selected for the CurrentMode attribute. Each + * item in this list represents a unique mode as indicated by the Mode field of the ModeOptionStruct. + * Each entry in this list shall have a unique value for the Mode field. Each entry in this list shall have a unique + * value for the Label field. + */ + public List supportedModes; // 0 list R V + /** + * Indicates the current mode of the server. + * The value of this field shall match the Mode field of one of the entries in the SupportedModes attribute. + * The value of this attribute may change at any time via an out-of-band interaction outside of the server, such as + * interactions with a user interface, via internal mode changes due to autonomously progressing through a sequence + * of operations, on system time-outs or idle delays, or via interactions coming from a fabric other than the one + * which last executed a ChangeToMode. + */ + public Integer currentMode; // 1 uint8 R V + /** + * Indicates the desired startup mode for the server when it is supplied with power. + * If this attribute is not null, the CurrentMode attribute shall be set to the StartUpMode value, when the server + * is powered up, except in the case when the OnMode attribute overrides the StartUpMode attribute (see + * OnModeWithPowerUp). + * This behavior does not apply to reboots associated with OTA. After an OTA restart, the CurrentMode attribute + * shall return to its value prior to the restart. + * The value of this field shall match the Mode field of one of the entries in the SupportedModes attribute. + * If this attribute is not implemented, or is set to the null value, it shall have no effect. + */ + public Integer startUpMode; // 2 uint8 RW VO + /** + * Indicates whether the value of CurrentMode depends on the state of the On/Off cluster on the same endpoint. If + * this attribute is not present or is set to null, there is no dependency, otherwise the CurrentMode attribute + * shall depend on the OnOff attribute in the On/Off cluster + * The value of this field shall match the Mode field of one of the entries in the SupportedModes attribute. + */ + public Integer onMode; // 3 uint8 RW VO + // Structs + + /** + * A Mode Tag is meant to be interpreted by the client for the purpose the cluster serves. + */ + public class ModeTagStruct { + /** + * If the MfgCode field exists, the Value field shall be in the manufacturer-specific value range (see Section + * 1.10.8, “Mode Namespace”). + * This field shall indicate the manufacturer’s VendorID and it shall determine the meaning of the Value field. + * The same manufacturer code and mode tag value in separate cluster instances are part of the same namespace + * and have the same meaning. For example: a manufacturer tag meaning "pinch" can be used both in a + * cluster whose purpose is to choose the amount of sugar, or in a cluster whose purpose is to choose the amount + * of salt. + */ + public Integer mfgCode; // vendor-id + /** + * This field shall indicate the mode tag within a mode tag namespace which is either manufacturer specific or + * standard. + */ + public ModeTag value; // ModeTag + + public ModeTagStruct(Integer mfgCode, ModeTag value) { + this.mfgCode = mfgCode; + this.value = value; + } + } + + /** + * This is a struct representing a possible mode of the server. + */ + public class ModeOptionStruct { + /** + * This field shall indicate readable text that describes the mode option, so that a client can provide it to + * the user to indicate what this option means. This field is meant to be readable and understandable by the + * user. + */ + public String label; // string + /** + * This field is used to identify the mode option. + */ + public Integer mode; // uint8 + /** + * This field shall contain a list of tags that are associated with the mode option. This may be used by clients + * to determine the full or the partial semantics of a certain mode, depending on which tags they understand, + * using standard definitions and/or manufacturer specific namespace definitions. + * The standard mode tags are defined in this cluster specification. For the derived cluster instances, if the + * specification of the derived cluster defines a namespace, the set of standard mode tags also includes the + * mode tag values from that namespace. + * Mode tags can help clients look for options that meet certain criteria, render the user interface, use the + * mode in an automation, or to craft help text their voice-driven interfaces. A mode tag shall be either a + * standard tag or a manufacturer specific tag, as defined in each ModeTagStruct list entry. + * A mode option may have more than one mode tag. A mode option may be associated with a mixture of standard and + * manufacturer specific mode tags. A mode option shall be associated with at least one standard mode tag. + * A few examples are provided below. + * • A mode named "100%" can have both the High (manufacturer specific) and Max (standard) mode tag. + * Clients seeking the mode for either High or Max will find the same mode in this case. + * • A mode that includes a LowEnergy tag can be displayed by the client using a widget icon that shows a green + * leaf. + * • A mode that includes a LowNoise tag may be used by the client when the user wishes for a lower level of + * audible sound, less likely to disturb the household’s activities. + * • A mode that includes a LowEnergy tag (standard, defined in this cluster specification) and also a Delicate + * tag (standard, defined in the namespace of a Laundry Mode derived cluster). + * • A mode that includes both a generic Quick tag (defined here), and Vacuum and Mop tags, (defined in the RVC + * Clean cluster that is a derivation of this cluster). + */ + public List modeTags; // list + + public ModeOptionStruct(String label, Integer mode, List modeTags) { + this.label = label; + this.mode = mode; + this.modeTags = modeTags; + } + } + + // Enums + public enum ModeChangeStatus implements MatterEnum { + SUCCESS(0, "Success"), + UNSUPPORTED_MODE(1, "Unsupported Mode"), + GENERIC_FAILURE(2, "Generic Failure"), + INVALID_IN_MODE(3, "Invalid In Mode"); + + public final Integer value; + public final String label; + + private ModeChangeStatus(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature creates a dependency between an OnOff cluster instance and this cluster instance on the same + * endpoint. See OnMode for more information. + */ + public boolean onOff; + + public FeatureMap(boolean onOff) { + this.onOff = onOff; + } + } + + protected ModeBaseCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used to change device modes. + * On receipt of this command the device shall respond with a ChangeToModeResponse command. + */ + public static ClusterCommand changeToMode(Integer newMode) { + Map map = new LinkedHashMap<>(); + if (newMode != null) { + map.put("newMode", newMode); + } + return new ClusterCommand("changeToMode", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "supportedModes : " + supportedModes + "\n"; + str += "currentMode : " + currentMode + "\n"; + str += "startUpMode : " + startUpMode + "\n"; + str += "onMode : " + onMode + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ModeSelectCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ModeSelectCluster.java new file mode 100644 index 00000000000..c947dc7f9d3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ModeSelectCluster.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ModeSelect + * + * @author Dan Cunningham - Initial contribution + */ +public class ModeSelectCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0050; + public static final String CLUSTER_NAME = "ModeSelect"; + public static final String CLUSTER_PREFIX = "modeSelect"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_DESCRIPTION = "description"; + public static final String ATTRIBUTE_STANDARD_NAMESPACE = "standardNamespace"; + public static final String ATTRIBUTE_SUPPORTED_MODES = "supportedModes"; + public static final String ATTRIBUTE_CURRENT_MODE = "currentMode"; + public static final String ATTRIBUTE_START_UP_MODE = "startUpMode"; + public static final String ATTRIBUTE_ON_MODE = "onMode"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute describes the purpose of the server, in readable text. + * For example, a coffee machine may have a Mode Select cluster for the amount of milk to add, and another Mode + * Select cluster for the amount of sugar to add. In this case, the first instance can have the description Milk and + * the second instance can have the description Sugar. This allows the user to tell the purpose of each of the + * instances. + */ + public String description; // 0 string R V + /** + * This attribute, when not null, shall indicate a single standard namespace for any standard semantic tag value + * supported in this or any other cluster instance with the same value of this attribute. A null value indicates no + * standard namespace, and therefore, no standard semantic tags are provided in this cluster instance. Each standard + * namespace and corresponding values and value meanings shall be defined in another document. + */ + public StandardNamespace standardNamespace; // 1 enum16 R V + /** + * This attribute is the list of supported modes that may be selected for the CurrentMode attribute. Each item in + * this list represents a unique mode as indicated by the Mode field of the ModeOptionStruct. Each entry in this + * list shall have a unique value for the Mode field. + */ + public List supportedModes; // 2 list R V + /** + * This attribute represents the current mode of the server. + * The value of this field must match the Mode field of one of the entries in the SupportedModes attribute. + */ + public Integer currentMode; // 3 uint8 R V + /** + * The StartUpMode attribute value indicates the desired startup mode for the server when it is supplied with power. + * If this attribute is not null, the CurrentMode attribute shall be set to the StartUpMode value, when the server + * is powered up, except in the case when the OnMode attribute overrides the StartUpMode attribute (see + * OnModeWithPowerUp). + * This behavior does not apply to reboots associated with OTA. After an OTA restart, the CurrentMode attribute + * shall return to its value prior to the restart. + * The value of this field shall match the Mode field of one of the entries in the SupportedModes attribute. + * If this attribute is not implemented, or is set to the null value, it shall have no effect. + */ + public Integer startUpMode; // 4 uint8 RW VO + /** + * Indicates the value of CurrentMode that depends on the state of the On/Off cluster on the same endpoint. If this + * attribute is not present or is set to null, it shall NOT have an effect, otherwise the CurrentMode attribute + * shall depend on the OnOff attribute of the On/Off cluster + * The value of this field shall match the Mode field of one of the entries in the SupportedModes attribute. + */ + public Integer onMode; // 5 uint8 RW VO + // Structs + + /** + * A Semantic Tag is meant to be interpreted by the client for the purpose the cluster serves. + */ + public class SemanticTagStruct { + /** + * This field shall indicate a manufacturer code (Vendor ID), and the Value field shall indicate a semantic tag + * defined by the manufacturer. Each manufacturer code supports a single namespace of values. The same + * manufacturer code and semantic tag value in separate cluster instances are part of the same namespace and + * have the same meaning. For example: a manufacturer tag meaning "pinch", has the same meaning in a + * cluster whose purpose is to choose the amount of sugar, or amount of salt. + */ + public Integer mfgCode; // vendor-id + /** + * This field shall indicate the semantic tag within a semantic tag namespace which is either manufacturer + * specific or standard. For semantic tags in a standard namespace, see Standard Namespace. + */ + public Integer value; // uint16 + + public SemanticTagStruct(Integer mfgCode, Integer value) { + this.mfgCode = mfgCode; + this.value = value; + } + } + + /** + * This is a struct representing a possible mode of the server. + */ + public class ModeOptionStruct { + /** + * This field is readable text that describes the mode option that can be used by a client to indicate to the + * user what this option means. This field is meant to be readable and understandable by the user. + */ + public String label; // string + /** + * The Mode field is used to identify the mode option. The value shall be unique for every item in the + * SupportedModes attribute. + */ + public Integer mode; // uint8 + /** + * This field is a list of semantic tags that map to the mode option. This may be used by clients to determine + * the meaning of the mode option as defined in a standard or manufacturer specific namespace. Semantic tags can + * help clients look for options that meet certain criteria. A semantic tag shall be either a standard tag or + * manufacturer specific tag as defined in each SemanticTagStruct list entry. + * A mode option may have more than one semantic tag. A mode option may be mapped to a mixture of standard and + * manufacturer specific semantic tags. + * All standard semantic tags are from a single namespace indicated by the StandardNamespace attribute. + * For example: A mode labeled "100%" can have both the HIGH (MS) and MAX (standard) semantic tag. + * Clients seeking the option for either HIGH or MAX will find the same option in this case. + */ + public List semanticTags; // list + + public ModeOptionStruct(String label, Integer mode, List semanticTags) { + this.label = label; + this.mode = mode; + this.semanticTags = semanticTags; + } + } + + // Enums + /** + * This attribute, when not null, shall indicate a single standard namespace for any standard semantic tag value + * supported in this or any other cluster instance with the same value of this attribute. A null value indicates no + * standard namespace, and therefore, no standard semantic tags are provided in this cluster instance. Each standard + * namespace and corresponding values and value meanings shall be defined in another document. + */ + public enum StandardNamespace implements MatterEnum { + DEFAULT(0, "Default"); + + public final Integer value; + public final String label; + + private StandardNamespace(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature creates a dependency between an OnOff cluster instance and this cluster instance on the same + * endpoint. See OnMode for more information. + */ + public boolean onOff; + + public FeatureMap(boolean onOff) { + this.onOff = onOff; + } + } + + public ModeSelectCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 80, "ModeSelect"); + } + + protected ModeSelectCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * On receipt of this command, if the NewMode field indicates a valid mode transition within the supported list, the + * server shall set the CurrentMode attribute to the NewMode value, otherwise, the server shall respond with an + * INVALID_COMMAND status response. + */ + public static ClusterCommand changeToMode(Integer newMode) { + Map map = new LinkedHashMap<>(); + if (newMode != null) { + map.put("newMode", newMode); + } + return new ClusterCommand("changeToMode", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "description : " + description + "\n"; + str += "standardNamespace : " + standardNamespace + "\n"; + str += "supportedModes : " + supportedModes + "\n"; + str += "currentMode : " + currentMode + "\n"; + str += "startUpMode : " + startUpMode + "\n"; + str += "onMode : " + onMode + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/NetworkCommissioningCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/NetworkCommissioningCluster.java new file mode 100644 index 00000000000..11022b3c1de --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/NetworkCommissioningCluster.java @@ -0,0 +1,601 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * NetworkCommissioning + * + * @author Dan Cunningham - Initial contribution + */ +public class NetworkCommissioningCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0031; + public static final String CLUSTER_NAME = "NetworkCommissioning"; + public static final String CLUSTER_PREFIX = "networkCommissioning"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_MAX_NETWORKS = "maxNetworks"; + public static final String ATTRIBUTE_NETWORKS = "networks"; + public static final String ATTRIBUTE_SCAN_MAX_TIME_SECONDS = "scanMaxTimeSeconds"; + public static final String ATTRIBUTE_CONNECT_MAX_TIME_SECONDS = "connectMaxTimeSeconds"; + public static final String ATTRIBUTE_INTERFACE_ENABLED = "interfaceEnabled"; + public static final String ATTRIBUTE_LAST_NETWORKING_STATUS = "lastNetworkingStatus"; + public static final String ATTRIBUTE_LAST_NETWORK_ID = "lastNetworkId"; + public static final String ATTRIBUTE_LAST_CONNECT_ERROR_VALUE = "lastConnectErrorValue"; + public static final String ATTRIBUTE_SUPPORTED_WI_FI_BANDS = "supportedWiFiBands"; + public static final String ATTRIBUTE_SUPPORTED_THREAD_FEATURES = "supportedThreadFeatures"; + public static final String ATTRIBUTE_THREAD_VERSION = "threadVersion"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This shall indicate the maximum number of network configuration entries that can be added, based on available + * device resources. The length of the Networks attribute shall be less than or equal to this value. + */ + public Integer maxNetworks; // 0 uint8 R A + /** + * Indicates the network configurations that are usable on the network interface represented by this cluster server + * instance. + * The order of configurations in the list reflects precedence. That is, any time the Node attempts to connect to + * the network it shall attempt to do so using the configurations in Networks Attribute in the order as they appear + * in the list. + * The order of list items shall only be modified by the AddOrUpdateThreadNetwork, AddOrUpdateWiFiNetwork and + * ReorderNetwork commands. In other words, the list shall be stable over time, unless mutated externally. + * Ethernet networks shall be automatically populated by the cluster server. Ethernet Network Commissioning Cluster + * instances shall always have exactly one NetworkInfoStruct instance in their Networks attribute. There shall be no + * way to add, update or remove Ethernet network configurations to those Cluster instances. + */ + public List networks; // 1 list R A + /** + * Indicates the maximum duration taken, in seconds, by the network interface on this cluster server instance to + * provide scan results. + * See ScanNetworks for usage. + */ + public Integer scanMaxTimeSeconds; // 2 uint8 R V + /** + * Indicates the maximum duration taken, in seconds, by the network interface on this cluster server instance to + * report a successful or failed network connection indication. This maximum time shall account for all operations + * needed until a successful network connection is deemed to have occurred, including, for example, obtaining IP + * addresses, or the execution of necessary internal retries. + */ + public Integer connectMaxTimeSeconds; // 3 uint8 R V + /** + * Indicates whether the associated network interface is enabled or not. By default all network interfaces SHOULD be + * enabled during initial commissioning (InterfaceEnabled set to true). + * It is undefined what happens if InterfaceEnabled is written to false on the same interface as that which is used + * to write the value. In that case, it is possible that the Administrator would have to await expiry of the + * fail-safe, and associated recovery of network configuration to prior safe values, before being able to + * communicate with the node again (see ArmFailSafe). + * It may be possible to disable Ethernet interfaces but it is implementation-defined. If not supported, a write to + * this attribute with a value of false shall fail with a status of INVALID_ACTION. When disabled, an Ethernet + * interface would longer employ media detection. That is, a simple unplug and replug of the cable shall NOT + * re-enable the interface. + * On Ethernet-only Nodes, there shall always be at least one of the Network Commissioning server cluster instances + * with InterfaceEnabled set to true. + */ + public Boolean interfaceEnabled; // 4 bool RW VA + /** + * Indicates the status of the last attempt either scan or connect to an operational network, using this interface, + * whether by invocation of the ConnectNetwork command or by autonomous connection after loss of connectivity or + * during initial establishment. If no such attempt was made, or no network configurations exist in the Networks + * attribute, then this attribute shall be set to null. + * This attribute is present to assist with error recovery during Network commissioning and to assist in + * non-concurrent networking commissioning flows. + */ + public NetworkCommissioningStatusEnum lastNetworkingStatus; // 5 NetworkCommissioningStatusEnum R A + /** + * Indicates the NetworkID used in the last attempt to connect to an operational network, using this interface, + * whether by invocation of the ConnectNetwork command or by autonomous connection after loss of connectivity or + * during initial establishment. If no such attempt was made, or no network configurations exist in the Networks + * attribute, then this attribute shall be set to null. + * If a network configuration is removed from the Networks attribute using the RemoveNetwork command after a + * connection attempt, this field may indicate a NetworkID that is no longer configured on the Node. + * This attribute is present to assist with error recovery during Network commissioning and to assist in + * non-concurrent networking commissioning flows. + */ + public OctetString lastNetworkId; // 6 octstr R A + /** + * Indicates the ErrorValue used in the last failed attempt to connect to an operational network, using this + * interface, whether by invocation of the ConnectNetwork command or by autonomous connection after loss of + * connectivity or during initial establishment. If no such attempt was made, or no network configurations exist in + * the Networks attribute, then this attribute shall be set to null. + * If the last connection succeeded, as indicated by a value of Success in the LastNetworkingStatus attribute, then + * this field shall be set to null. + * This attribute is present to assist with error recovery during Network commissioning and to assist in + * non-concurrent networking commissioning flows. + */ + public Integer lastConnectErrorValue; // 7 int32 R A + /** + * Indicates all the frequency bands supported by the Wi-Fi interface configured by the cluster instance. + */ + public List supportedWiFiBands; // 8 list R V + /** + * Indicates all of the Thread features supported by the Thread interface configured by the cluster instance. + * This attribute is primarily used to determine the most important general capabilities of the Thread interface + * associated with the cluster instance, as opposed to the current runtime dynamic configuration. Note that most + * run-time details of the actual Thread interface are found in the Thread Network Diagnostics cluster, if + * supported. + */ + public ThreadCapabilitiesBitmap supportedThreadFeatures; // 9 ThreadCapabilitiesBitmap R V + /** + * Indicates the Thread version supported by the Thread interface configured by the cluster instance. + * The format shall match the value mapping found in the "Version TLV" section of Thread specification. + * For example, Thread 1.3.0 would have ThreadVersion set to 4. + */ + public Integer threadVersion; // 10 uint16 R V + // Structs + + /** + * NetworkInfoStruct struct describes an existing network configuration, as provided in the Networks attribute. + */ + public class NetworkInfoStruct { + /** + * Every network is uniquely identified (for purposes of commissioning) by a NetworkID mapping to the following + * technology-specific properties: + * • SSID for Wi-Fi + * • Extended PAN ID for Thread + * • Network interface instance name at operating system (or equivalent unique name) for Ethernet. + * The semantics of the NetworkID field therefore varies between network types accordingly. It contains SSID for + * Wi-Fi networks, Extended PAN ID (XPAN ID) for Thread networks and netif name for Ethernet networks. + * > [!NOTE] + * > SSID in Wi-Fi is a collection of 1-32 bytes, the text encoding of which is not specified. + * Implementations must be careful to support reporting byte strings without requiring a particular encoding for + * transfer. Only the commissioner should try to potentially decode the bytes. The most common encoding is + * UTF-8, however this is just a convention. Some configurations may use Latin-1 or other character sets. A + * commissioner may decode using UTF-8, replacing encoding errors with "?" at the application level + * while retaining the underlying representation. + * XPAN ID is a big-endian 64-bit unsigned number, represented on the first 8 octets of the octet string. + */ + public OctetString networkId; // octstr + /** + * This field shall indicate the connected status of the associated network, where "connected" means + * currently linked to the network technology (e.g. Associated for a Wi-Fi network, media connected for an + * Ethernet network). + */ + public Boolean connected; // bool + + public NetworkInfoStruct(OctetString networkId, Boolean connected) { + this.networkId = networkId; + this.connected = connected; + } + } + + /** + * WiFiInterfaceScanResultStruct represents a single Wi-Fi network scan result. + */ + public class WiFiInterfaceScanResultStruct { + public WiFiSecurityBitmap security; // WiFiSecurityBitmap + public OctetString ssid; // octstr + public OctetString bssid; // octstr + public Integer channel; // uint16 + /** + * This field, if present, may be used to differentiate overlapping channel number values across different Wi-Fi + * frequency bands. + */ + public WiFiBandEnum wiFiBand; // WiFiBandEnum + /** + * This field, if present, shall denote the signal strength in dBm of the associated scan result. + */ + public Integer rssi; // int8 + + public WiFiInterfaceScanResultStruct(WiFiSecurityBitmap security, OctetString ssid, OctetString bssid, + Integer channel, WiFiBandEnum wiFiBand, Integer rssi) { + this.security = security; + this.ssid = ssid; + this.bssid = bssid; + this.channel = channel; + this.wiFiBand = wiFiBand; + this.rssi = rssi; + } + } + + /** + * ThreadInterfaceScanResultStruct represents a single Thread network scan result. + */ + public class ThreadInterfaceScanResultStruct { + public Integer panId; // uint16 + public BigInteger extendedPanId; // uint64 + public String networkName; // string + public Integer channel; // uint16 + public Integer version; // uint8 + /** + * ExtendedAddress stands for an IEEE 802.15.4 Extended Address. + */ + public OctetString extendedAddress; // hwadr + public Integer rssi; // int8 + public Integer lqi; // uint8 + + public ThreadInterfaceScanResultStruct(Integer panId, BigInteger extendedPanId, String networkName, + Integer channel, Integer version, OctetString extendedAddress, Integer rssi, Integer lqi) { + this.panId = panId; + this.extendedPanId = extendedPanId; + this.networkName = networkName; + this.channel = channel; + this.version = version; + this.extendedAddress = extendedAddress; + this.rssi = rssi; + this.lqi = lqi; + } + } + + // Enums + /** + * WiFiBandEnum encodes a supported Wi-Fi frequency band present in the WiFiBand field of the + * WiFiInterfaceScanResultStruct. + */ + public enum WiFiBandEnum implements MatterEnum { + V2G4(0, "2 G 4"), + V3G65(1, "3 G 65"), + V5G(2, "5 G"), + V6G(3, "6 G"), + V60G(4, "60 G"), + V1G(5, "1 G"); + + public final Integer value; + public final String label; + + private WiFiBandEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum NetworkCommissioningStatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + OUT_OF_RANGE(1, "Out Of Range"), + BOUNDS_EXCEEDED(2, "Bounds Exceeded"), + NETWORK_ID_NOT_FOUND(3, "Network Id Not Found"), + DUPLICATE_NETWORK_ID(4, "Duplicate Network Id"), + NETWORK_NOT_FOUND(5, "Network Not Found"), + REGULATORY_ERROR(6, "Regulatory Error"), + AUTH_FAILURE(7, "Auth Failure"), + UNSUPPORTED_SECURITY(8, "Unsupported Security"), + OTHER_CONNECTION_FAILURE(9, "Other Connection Failure"), + IPV6FAILED(10, "Ipv 6 Failed"), + IP_BIND_FAILED(11, "Ip Bind Failed"), + UNKNOWN_ERROR(12, "Unknown Error"); + + public final Integer value; + public final String label; + + private NetworkCommissioningStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + /** + * WiFiSecurityBitmap encodes the supported Wi-Fi security types present in the Security field of the + * WiFiInterfaceScanResultStruct. + */ + public static class WiFiSecurityBitmap { + public boolean unencrypted; + public boolean wep; + public boolean wpaPersonal; + public boolean wpa2Personal; + public boolean wpa3Personal; + + public WiFiSecurityBitmap(boolean unencrypted, boolean wep, boolean wpaPersonal, boolean wpa2Personal, + boolean wpa3Personal) { + this.unencrypted = unencrypted; + this.wep = wep; + this.wpaPersonal = wpaPersonal; + this.wpa2Personal = wpa2Personal; + this.wpa3Personal = wpa3Personal; + } + } + + /** + * The ThreadCapabilitiesBitmap encodes the supported Thread features and capabilities of a Thread-enabled network + * interface. + * > [!NOTE] + * > The valid combinations of capabilities are restricted and dependent on Thread version. + */ + public static class ThreadCapabilitiesBitmap { + public boolean isBorderRouterCapable; + public boolean isRouterCapable; + public boolean isSleepyEndDeviceCapable; + public boolean isFullThreadDevice; + public boolean isSynchronizedSleepyEndDeviceCapable; + + public ThreadCapabilitiesBitmap(boolean isBorderRouterCapable, boolean isRouterCapable, + boolean isSleepyEndDeviceCapable, boolean isFullThreadDevice, + boolean isSynchronizedSleepyEndDeviceCapable) { + this.isBorderRouterCapable = isBorderRouterCapable; + this.isRouterCapable = isRouterCapable; + this.isSleepyEndDeviceCapable = isSleepyEndDeviceCapable; + this.isFullThreadDevice = isFullThreadDevice; + this.isSynchronizedSleepyEndDeviceCapable = isSynchronizedSleepyEndDeviceCapable; + } + } + + public static class FeatureMap { + /** + * + * Wi-Fi related features + */ + public boolean wiFiNetworkInterface; + /** + * + * Thread related features + */ + public boolean threadNetworkInterface; + /** + * + * Ethernet related features + */ + public boolean ethernetNetworkInterface; + + public FeatureMap(boolean wiFiNetworkInterface, boolean threadNetworkInterface, + boolean ethernetNetworkInterface) { + this.wiFiNetworkInterface = wiFiNetworkInterface; + this.threadNetworkInterface = threadNetworkInterface; + this.ethernetNetworkInterface = ethernetNetworkInterface; + } + } + + public NetworkCommissioningCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 49, "NetworkCommissioning"); + } + + protected NetworkCommissioningCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall scan on the Cluster instance’s associated network interface for either of: + * • All available networks (non-directed scanning) + * • Specific networks (directed scanning) + * Scanning for available networks detects all networks of the type corresponding to the cluster server instance’s + * associated network interface that are possible to join, such as all visible Wi-Fi access points for Wi-Fi cluster + * server instances, all Thread PANs for Thread cluster server instances, within bounds of the maximum response + * size. + * Scanning for a specific network (i.e. directed scanning) takes place if a network identifier (e.g. Wi-Fi SSID) is + * provided in the command arguments. Directed scanning shall restrict the result set to the specified network only. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * The client shall NOT expect the server to be done scanning and have responded with ScanNetworksResponse before + * ScanMaxTimeSeconds seconds have elapsed. Enough transport time affordances for retries SHOULD be expected before + * a client determines the operation to have timed-out. + * This command shall fail with a status code of BUSY if the server determines that it will fail to reliably send a + * response due to changes of networking interface configuration at runtime for the interface over which the command + * was invoked, or if it is currently unable to proceed with such an operation. + * For Wi-Fi-supporting servers (WI feature) the server shall always honor directed scans, and attempt to provide + * all matching BSSID which are reachable on the bands which would otherwise be attempted if a ConnectNetwork having + * the specified SSID were to take place. This command is useful for clients to determine reachability capabilities + * as seen by the server’s own radios. + * For Wi-Fi-supporting servers the server shall always scan on all bands supported by the interface associated with + * the cluster instance on which the command was invoked. + * If the command was invoked over the same link whose configuration is managed by a given server cluster instance, + * there may be an impact on other communication from the invoking client, as well as other clients, while the + * network interface is processing the scan. Clients SHOULD NOT use this command unless actively in the process of + * re-configuring network connectivity. + */ + public static ClusterCommand scanNetworks(OctetString ssid, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (ssid != null) { + map.put("ssid", ssid); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("scanNetworks", map); + } + + /** + * This command shall be used to add or modify Wi-Fi network configurations. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * The Credentials associated with the network are not readable after execution of this command, as they do not + * appear in the Networks attribute, for security reasons. + * If this command contains a ClientIdentifier, and the Networks list does not contain an entry with a matching + * ClientIdentifier, then this command shall fail with a status of NOT_FOUND. + * See Section 11.9.7.5, “Common processing of AddOrUpdateWiFiNetwork and AddOrUpdateThreadNetwork” for behavior of + * addition/update. + */ + public static ClusterCommand addOrUpdateWiFiNetwork(OctetString ssid, OctetString credentials, + BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (ssid != null) { + map.put("ssid", ssid); + } + if (credentials != null) { + map.put("credentials", credentials); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("addOrUpdateWiFiNetwork", map); + } + + /** + * This command shall be used to add or modify Thread network configurations. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * See Section 11.9.7.5, “Common processing of AddOrUpdateWiFiNetwork and AddOrUpdateThreadNetwork” for behavior of + * addition/update. + * The XPAN ID in the OperationalDataset serves as the NetworkID for the network configuration to be added or + * updated. + * If the Networks attribute does not contain an entry with the same NetworkID as the one provided in the + * OperationalDataset, the operation shall be considered an addition, otherwise, it shall be considered an update. + */ + public static ClusterCommand addOrUpdateThreadNetwork(OctetString operationalDataset, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (operationalDataset != null) { + map.put("operationalDataset", operationalDataset); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("addOrUpdateThreadNetwork", map); + } + + /** + * This command shall remove the network configuration from the Cluster if there was already a network configuration + * with the same NetworkID. The relative order of the entries in the Networks attribute shall remain unchanged, + * except for the removal of the requested network configuration. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * If the Networks attribute does not contain a matching entry, the command shall immediately respond with + * NetworkConfigResponse having NetworkingStatus status field set to NetworkIdNotFound. + * On success, the NetworkConfigResponse command shall have its NetworkIndex field set to the 0- based index of the + * entry in the Networks attribute that was just removed, and a NetworkingStatus status field set to Success. + */ + public static ClusterCommand removeNetwork(OctetString networkId, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (networkId != null) { + map.put("networkId", networkId); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("removeNetwork", map); + } + + /** + * This command shall attempt to connect to a network whose configuration was previously added by either the + * AddOrUpdateWiFiNetwork or AddOrUpdateThreadNetwork commands. Network is identified by its NetworkID. + * This command shall fail with a BUSY status code returned to the initiator if the server is currently unable to + * proceed with such an operation, such as if it is currently attempting to connect in the background, or is already + * proceeding with a prior ConnectNetwork. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * Before connecting to the new network, the Node shall disconnect the operational network connections managed by + * any other Network Commissioning cluster instances (whether under the Root Node or a Secondary Network Interface), + * where those connections are not represented by an entry in the Networks attribute of the corresponding cluster + * instance. This ensures that an Administrator or Commissioner can reliably reconfigure the operational network + * connection of a device that has one or more Secondary Network interfaces, for example by removing the active + * network configuration from one cluster instance, followed by adding a new configuration and calling + * ConnectNetwork on a different cluster instance. + * Success or failure of this command shall be communicated by the ConnectNetworkResponse command, unless some data + * model validations caused a FAILURE status to be sent prior to finishing execution of the command. The + * ConnectNetworkResponse shall indicate the value Success in the NetworkingStatus field on successful connection. + * On failure to connect, the ConnectNetworkResponse shall contain an appropriate NetworkingStatus, DebugText and + * ErrorValue indicating the reason for failure. + * The amount of time needed to determine successful or failing connectivity on the cluster server’s associated + * interface is provided by the ConnectMaxTimeSeconds attribute. Clients shall NOT consider the connection to have + * timed-out until at least that duration has taken place. For non-concurrent commissioning situations, the client + * SHOULD allow additional margin of time to account for its delay in executing operational discovery of the Node + * once it is connected to the new network. + * On successful connection, the entry associated with the given Network configuration in the Networks attribute + * shall indicate its Connected field set to true, and all other entries, if any exist, shall indicate their + * Connected field set to false. + * On failure to connect, the entry associated with the given Network configuration in the Networks attribute shall + * indicate its Connected field set to false. + * The precedence order of any entry subject to ConnectNetwork shall NOT change within the Networks attribute. + * Even after successfully connecting to a network, the configuration shall revert to the prior state of + * configuration if the CommissioningComplete command (see CommissioningComplete) is not successfully invoked before + * expiry of the Fail-Safe timer. + * When non-concurrent commissioning is being used by a Commissioner or Administrator, the ConnectNetworkResponse + * shall be sent with the NetworkingStatus field set to Success prior to closing the commissioning channel, even if + * not yet connected to the operational network, unless the device would be incapable of joining that network, in + * which case the usual failure path described in the prior paragraphs shall be followed. Once the commissioning + * channel is closed, the operational channel will be started. It is possible that the only method to determine + * success of the operation is operational discovery of the Node on the new operational network. Therefore, before + * invoking the ConnectNetwork command, the client SHOULD re-invoke the Arm Fail-Safe command with a duration that + * meets the following: + * 1. Sufficient time to meet the minimum required time (see ConnectMaxTimeSeconds) that may be taken by the server + * to connect to the desired network. + * 2. Sufficient time to account for possible message-layer retries when a response is requested. + * 3. Sufficient time to allow operational discovery on the new network by a Commissioner or Administrator. + * 4. Sufficient time to establish a CASE session after operational discovery + * 5. Not so long that, in error situations, the delay to reverting back to being discoverable for commissioning + * with a previous configuration would cause significant user-perceived delay. + * Note as well that the CommissioningTimeout duration provided in a prior OpenCommissioningWindow or + * OpenBasicCommissioningWindow command may impact the total time available to proceed with error recovery after a + * connection failure. + * The LastNetworkingStatus, LastNetworkID and LastConnectErrorValue attributes may assist the client in determining + * the reason for a failure after reconnecting over a Commissioning channel, especially in non-concurrent + * commissioning situations. + */ + public static ClusterCommand connectNetwork(OctetString networkId, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (networkId != null) { + map.put("networkId", networkId); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("connectNetwork", map); + } + + /** + * This command shall set the specific order of the network configuration selected by its NetworkID in the Networks + * attribute to match the position given by NetworkIndex. + */ + public static ClusterCommand reorderNetwork(OctetString networkId, Integer networkIndex, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (networkId != null) { + map.put("networkId", networkId); + } + if (networkIndex != null) { + map.put("networkIndex", networkIndex); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("reorderNetwork", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "maxNetworks : " + maxNetworks + "\n"; + str += "networks : " + networks + "\n"; + str += "scanMaxTimeSeconds : " + scanMaxTimeSeconds + "\n"; + str += "connectMaxTimeSeconds : " + connectMaxTimeSeconds + "\n"; + str += "interfaceEnabled : " + interfaceEnabled + "\n"; + str += "lastNetworkingStatus : " + lastNetworkingStatus + "\n"; + str += "lastNetworkId : " + lastNetworkId + "\n"; + str += "lastConnectErrorValue : " + lastConnectErrorValue + "\n"; + str += "supportedWiFiBands : " + supportedWiFiBands + "\n"; + str += "supportedThreadFeatures : " + supportedThreadFeatures + "\n"; + str += "threadVersion : " + threadVersion + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/NitrogenDioxideConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/NitrogenDioxideConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..be797b7269f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/NitrogenDioxideConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * NitrogenDioxideConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class NitrogenDioxideConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x0413; + public static final String CLUSTER_NAME = "NitrogenDioxideConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "nitrogenDioxideConcentrationMeasurement"; + + public NitrogenDioxideConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1043, "NitrogenDioxideConcentrationMeasurement"); + } + + protected NitrogenDioxideConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OccupancySensingCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OccupancySensingCluster.java new file mode 100644 index 00000000000..98ee88cde92 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OccupancySensingCluster.java @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * OccupancySensing + * + * @author Dan Cunningham - Initial contribution + */ +public class OccupancySensingCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0406; + public static final String CLUSTER_NAME = "OccupancySensing"; + public static final String CLUSTER_PREFIX = "occupancySensing"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_OCCUPANCY = "occupancy"; + public static final String ATTRIBUTE_OCCUPANCY_SENSOR_TYPE = "occupancySensorType"; + public static final String ATTRIBUTE_OCCUPANCY_SENSOR_TYPE_BITMAP = "occupancySensorTypeBitmap"; + public static final String ATTRIBUTE_HOLD_TIME = "holdTime"; + public static final String ATTRIBUTE_HOLD_TIME_LIMITS = "holdTimeLimits"; + public static final String ATTRIBUTE_PIR_OCCUPIED_TO_UNOCCUPIED_DELAY = "pirOccupiedToUnoccupiedDelay"; + public static final String ATTRIBUTE_PIR_UNOCCUPIED_TO_OCCUPIED_DELAY = "pirUnoccupiedToOccupiedDelay"; + public static final String ATTRIBUTE_PIR_UNOCCUPIED_TO_OCCUPIED_THRESHOLD = "pirUnoccupiedToOccupiedThreshold"; + public static final String ATTRIBUTE_ULTRASONIC_OCCUPIED_TO_UNOCCUPIED_DELAY = "ultrasonicOccupiedToUnoccupiedDelay"; + public static final String ATTRIBUTE_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_DELAY = "ultrasonicUnoccupiedToOccupiedDelay"; + public static final String ATTRIBUTE_ULTRASONIC_UNOCCUPIED_TO_OCCUPIED_THRESHOLD = "ultrasonicUnoccupiedToOccupiedThreshold"; + public static final String ATTRIBUTE_PHYSICAL_CONTACT_OCCUPIED_TO_UNOCCUPIED_DELAY = "physicalContactOccupiedToUnoccupiedDelay"; + public static final String ATTRIBUTE_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_DELAY = "physicalContactUnoccupiedToOccupiedDelay"; + public static final String ATTRIBUTE_PHYSICAL_CONTACT_UNOCCUPIED_TO_OCCUPIED_THRESHOLD = "physicalContactUnoccupiedToOccupiedThreshold"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the sensed (processed) status of occupancy. For compatibility reasons this is expressed as a bitmap + * where the status is indicated in bit 0: a value of 1 means occupied, and 0 means unoccupied, with the other bits + * set to 0; this can be considered equivalent to a boolean. + */ + public OccupancyBitmap occupancy; // 0 OccupancyBitmap R V + public OccupancySensorTypeEnum occupancySensorType; // 1 OccupancySensorTypeEnum R V + public OccupancySensorTypeBitmap occupancySensorTypeBitmap; // 2 OccupancySensorTypeBitmap R V + /** + * This attribute shall specify the time delay, in seconds, before the sensor changes to its unoccupied state after + * the last detection of occupancy in the sensed area. This is equivalent to the legacy + * OccupiedToUnoccupiedDelay attributes. + * The value of HoldTime shall be within the limits provided in the HoldTimeLimits attribute, i.e. HoldTimeMin + * <= HoldTime <= HoldTimeMax Low values of HoldTime SHOULD be avoided since they could lead to many + * reporting messages. A value 0 for HoldTime shall NOT be used. + * The figure below illustrates this with an example of how this attribute is used for a PIR sensor. It uses + * threshold detection to generate an "internal detection" signal, which needs post-processing to become + * usable for transmission (traffic shaping). The bit in the Occupancy attribute will be set to 1 when the internal + * detection signal goes high, and will stay at 1 for HoldTime after the (last) instance where the internal + * detection signal goes low. + * The top half of the figure shows the case of a single trigger: the bit in the Occupancy attribute will be 1 for + * the duration of the PIR signal exceeding the threshold plus HoldTime. The bottom half of the figure shows the + * case of multiple triggers: the second trigger starts before the HoldTime of the first trigger has expired; this + * results in a single period of the bit in the Occupancy attribute being 1. The bit in the Occupancy attribute will + * be set to 1 from the start of the first period where the PIR signal exceeds the threshold until HoldTime after + * the last moment where the PIR exceeded the threshold. + */ + public Integer holdTime; // 3 uint16 RW VM + /** + * Indicates the server’s limits, and default value, for the HoldTime attribute. + */ + public HoldTimeLimitsStruct holdTimeLimits; // 4 HoldTimeLimitsStruct R V + /** + * This attribute shall specify the time delay, in seconds, before the PIR sensor changes to its unoccupied state + * after the last detection of occupancy in the sensed area. + */ + public Integer pirOccupiedToUnoccupiedDelay; // 16 uint16 RW VM + /** + * This attribute shall specify the time delay, in seconds, before the PIR sensor changes to its occupied state + * after the first detection of occupancy in the sensed area. + */ + public Integer pirUnoccupiedToOccupiedDelay; // 17 uint16 RW VM + /** + * This attribute shall specify the number of occupancy detection events that must occur in the period + * PIRUnoccupiedToOccupiedDelay, before the PIR sensor changes to its occupied state. + */ + public Integer pirUnoccupiedToOccupiedThreshold; // 18 uint8 RW VM + /** + * This attribute shall specify the time delay, in seconds, before the Ultrasonic sensor changes to its unoccupied + * state after the last detection of occupancy in the sensed area. + */ + public Integer ultrasonicOccupiedToUnoccupiedDelay; // 32 uint16 RW VM + /** + * This attribute shall specify the time delay, in seconds, before the Ultrasonic sensor changes to its occupied + * state after the first detection of occupancy in the sensed area. + */ + public Integer ultrasonicUnoccupiedToOccupiedDelay; // 33 uint16 RW VM + /** + * This attribute shall specify the number of occupancy detection events that must occur in the period + * UltrasonicUnoccupiedToOccupiedDelay, before the Ultrasonic sensor changes to its occupied state. + */ + public Integer ultrasonicUnoccupiedToOccupiedThreshold; // 34 uint8 RW VM + /** + * This attribute shall specify the time delay, in seconds, before the physical contact occupancy sensor changes to + * its unoccupied state after detecting the unoccupied event. + */ + public Integer physicalContactOccupiedToUnoccupiedDelay; // 48 uint16 RW VM + /** + * This attribute shall specify the time delay, in seconds, before the physical contact sensor changes to its + * occupied state after the first detection of the occupied event. + */ + public Integer physicalContactUnoccupiedToOccupiedDelay; // 49 uint16 RW VM + /** + * This attribute shall specify the number of occupancy detection events that must occur in the period + * PhysicalContactUnoccupiedToOccupiedDelay, before the PhysicalContact sensor changes to its occupied state. + */ + public Integer physicalContactUnoccupiedToOccupiedThreshold; // 50 uint8 RW VM + // Structs + + /** + * If this event is supported, it shall be generated when the Occupancy attribute changes. + */ + public class OccupancyChanged { + /** + * This field shall indicate the new value of the Occupancy attribute. + */ + public OccupancyBitmap occupancy; // OccupancyBitmap + + public OccupancyChanged(OccupancyBitmap occupancy) { + this.occupancy = occupancy; + } + } + + /** + * This structure provides information on the server’s supported values for the HoldTime attribute. + */ + public class HoldTimeLimitsStruct { + /** + * This field shall specify the minimum value of the server’s supported value for the HoldTime attribute, in + * seconds. + */ + public Integer holdTimeMin; // uint16 + /** + * This field shall specify the maximum value of the server’s supported value for the HoldTime attribute, in + * seconds. + */ + public Integer holdTimeMax; // uint16 + /** + * This field shall specify the (manufacturer-determined) default value of the server’s HoldTime attribute, in + * seconds. This is the value that a client who wants to reset the settings to a valid default SHOULD use. + */ + public Integer holdTimeDefault; // uint16 + + public HoldTimeLimitsStruct(Integer holdTimeMin, Integer holdTimeMax, Integer holdTimeDefault) { + this.holdTimeMin = holdTimeMin; + this.holdTimeMax = holdTimeMax; + this.holdTimeDefault = holdTimeDefault; + } + } + + // Enums + /** + * > [!NOTE] + * > This enum is as defined in ClusterRevision 4 and its definition shall NOT be extended; the feature flags + * provide the sensor modality (or modalities) for later cluster revisions. See Backward Compatibility section. + */ + public enum OccupancySensorTypeEnum implements MatterEnum { + PIR(0, "Pir"), + ULTRASONIC(1, "Ultrasonic"), + PIR_AND_ULTRASONIC(2, "Pir And Ultrasonic"), + PHYSICAL_CONTACT(3, "Physical Contact"); + + public final Integer value; + public final String label; + + private OccupancySensorTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class OccupancyBitmap { + /** + * Indicates the sensed occupancy state + * If this bit is set, it shall indicate the occupied state else if the bit if not set, it shall indicate the + * unoccupied state. + */ + public boolean occupied; + + public OccupancyBitmap(boolean occupied) { + this.occupied = occupied; + } + } + + /** + * > [!NOTE] + * > This enum is as defined in ClusterRevision 4 and its definition shall NOT be extended; the feature flags + * provide the sensor modality (or modalities) for later cluster revisions. See Backward Compatibility section. + */ + public static class OccupancySensorTypeBitmap { + public boolean pir; + public boolean ultrasonic; + public boolean physicalContact; + + public OccupancySensorTypeBitmap(boolean pir, boolean ultrasonic, boolean physicalContact) { + this.pir = pir; + this.ultrasonic = ultrasonic; + this.physicalContact = physicalContact; + } + } + + public static class FeatureMap { + /** + * + * Supports sensing using a modality not listed in the other bits + */ + public boolean other; + /** + * + * Supports sensing using PIR (Passive InfraRed) + */ + public boolean passiveInfrared; + /** + * + * Supports sensing using UltraSound + */ + public boolean ultrasonic; + /** + * + * Supports sensing using a physical contact + */ + public boolean physicalContact; + /** + * + * Supports sensing using Active InfraRed measurement (e.g. time-of-flight or transflective/reflec tive IR + * sensing) + */ + public boolean activeInfrared; + /** + * + * Supports sensing using radar waves (microwave) + */ + public boolean radar; + /** + * + * Supports sensing based on RF signal analysis + */ + public boolean rfSensing; + /** + * + * Supports sensing based on analyzing images + */ + public boolean vision; + + public FeatureMap(boolean other, boolean passiveInfrared, boolean ultrasonic, boolean physicalContact, + boolean activeInfrared, boolean radar, boolean rfSensing, boolean vision) { + this.other = other; + this.passiveInfrared = passiveInfrared; + this.ultrasonic = ultrasonic; + this.physicalContact = physicalContact; + this.activeInfrared = activeInfrared; + this.radar = radar; + this.rfSensing = rfSensing; + this.vision = vision; + } + } + + public OccupancySensingCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1030, "OccupancySensing"); + } + + protected OccupancySensingCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "occupancy : " + occupancy + "\n"; + str += "occupancySensorType : " + occupancySensorType + "\n"; + str += "occupancySensorTypeBitmap : " + occupancySensorTypeBitmap + "\n"; + str += "holdTime : " + holdTime + "\n"; + str += "holdTimeLimits : " + holdTimeLimits + "\n"; + str += "pirOccupiedToUnoccupiedDelay : " + pirOccupiedToUnoccupiedDelay + "\n"; + str += "pirUnoccupiedToOccupiedDelay : " + pirUnoccupiedToOccupiedDelay + "\n"; + str += "pirUnoccupiedToOccupiedThreshold : " + pirUnoccupiedToOccupiedThreshold + "\n"; + str += "ultrasonicOccupiedToUnoccupiedDelay : " + ultrasonicOccupiedToUnoccupiedDelay + "\n"; + str += "ultrasonicUnoccupiedToOccupiedDelay : " + ultrasonicUnoccupiedToOccupiedDelay + "\n"; + str += "ultrasonicUnoccupiedToOccupiedThreshold : " + ultrasonicUnoccupiedToOccupiedThreshold + "\n"; + str += "physicalContactOccupiedToUnoccupiedDelay : " + physicalContactOccupiedToUnoccupiedDelay + "\n"; + str += "physicalContactUnoccupiedToOccupiedDelay : " + physicalContactUnoccupiedToOccupiedDelay + "\n"; + str += "physicalContactUnoccupiedToOccupiedThreshold : " + physicalContactUnoccupiedToOccupiedThreshold + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OnOffCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OnOffCluster.java new file mode 100644 index 00000000000..b3f72c86f27 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OnOffCluster.java @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * OnOff + * + * @author Dan Cunningham - Initial contribution + */ +public class OnOffCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0006; + public static final String CLUSTER_NAME = "OnOff"; + public static final String CLUSTER_PREFIX = "onOff"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_ON_OFF = "onOff"; + public static final String ATTRIBUTE_GLOBAL_SCENE_CONTROL = "globalSceneControl"; + public static final String ATTRIBUTE_ON_TIME = "onTime"; + public static final String ATTRIBUTE_OFF_WAIT_TIME = "offWaitTime"; + public static final String ATTRIBUTE_START_UP_ON_OFF = "startUpOnOff"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute indicates whether the device type implemented on the endpoint is turned off or turned on, in these + * cases the value of the OnOff attribute equals FALSE, or TRUE respectively. + */ + public Boolean onOff; // 0 bool R V + /** + * In order to support the use case where the user gets back the last setting of a set of devices (e.g. level + * settings for lights), a global scene is introduced which is stored when the devices are turned off and recalled + * when the devices are turned on. The global scene is defined as the scene that is stored with group identifier 0 + * and scene identifier 0. + * This attribute is defined in order to prevent a second Off command storing the all-devices-off situation as a + * global scene, and to prevent a second On command destroying the current settings by going back to the global + * scene. + * This attribute shall be set to TRUE after the reception of a command which causes the OnOff attribute to be set + * to TRUE, such as a standard On command, a MoveToLevel(WithOnOff) command, a RecallScene command or a + * OnWithRecallGlobalScene command. + * This attribute is set to FALSE after reception of a OffWithEffect command. + */ + public Boolean globalSceneControl; // 16384 bool R V + /** + * This attribute specifies the length of time (in 1/10ths second) that the On state shall be maintained before + * automatically transitioning to the Off state when using the OnWithTimedOff command. This attribute can be written + * at any time, but writing a value only has effect when in the Timed On state. See OnWithTimedOff for more details. + */ + public Integer onTime; // 16385 uint16 RW VO + /** + * This attribute specifies the length of time (in 1/10ths second) that the Off state shall be guarded to prevent + * another OnWithTimedOff command turning the server back to its On state (e.g., when leaving a room, the lights are + * turned off but an occupancy sensor detects the leaving person and attempts to turn the lights back on). This + * attribute can be written at any time, but writing a value only has an effect when in the Timed On state followed + * by a transition to the Delayed Off state, or in the Delayed Off state. See OnWithTimedOff for more details. + */ + public Integer offWaitTime; // 16386 uint16 RW VO + /** + * This attribute shall define the desired startup behavior of a device when it is supplied with power and this + * state shall be reflected in the OnOff attribute. If the value is null, the OnOff attribute is set to its previous + * value. Otherwise, the behavior is defined in the table defining StartUpOnOffEnum. + * This behavior does not apply to reboots associated with OTA. After an OTA restart, the OnOff attribute shall + * return to its value prior to the restart. + */ + public StartUpOnOffEnum startUpOnOff; // 16387 StartUpOnOffEnum RW VM + + // Enums + public enum StartUpOnOffEnum implements MatterEnum { + OFF(0, "Off"), + ON(1, "On"), + TOGGLE(2, "Toggle"); + + public final Integer value; + public final String label; + + private StartUpOnOffEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EffectIdentifierEnum implements MatterEnum { + DELAYED_ALL_OFF(0, "Delayed All Off"), + DYING_LIGHT(1, "Dying Light"); + + public final Integer value; + public final String label; + + private EffectIdentifierEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum DelayedAllOffEffectVariantEnum implements MatterEnum { + DELAYED_OFF_FAST_FADE(0, "Delayed Off Fast Fade"), + NO_FADE(1, "No Fade"), + DELAYED_OFF_SLOW_FADE(2, "Delayed Off Slow Fade"); + + public final Integer value; + public final String label; + + private DelayedAllOffEffectVariantEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum DyingLightEffectVariantEnum implements MatterEnum { + DYING_LIGHT_FADE_OFF(0, "Dying Light Fade Off"); + + public final Integer value; + public final String label; + + private DyingLightEffectVariantEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class OnOffControlBitmap { + public boolean acceptOnlyWhenOn; + + public OnOffControlBitmap(boolean acceptOnlyWhenOn) { + this.acceptOnlyWhenOn = acceptOnlyWhenOn; + } + } + + public static class FeatureMap { + /** + * + * This cluster is used for a lighting application. + * On receipt of a Level Control cluster command that causes the OnOff attribute to be set to FALSE, the OnTime + * attribute shall be set to 0. + * On receipt of a Level Control cluster command that causes the OnOff attribute to be set to TRUE, if the value + * of the OnTime attribute is equal to 0, the server shall set the OffWaitTime attribute to 0. + */ + public boolean lighting; + /** + * + * When this feature is supported, the device exposing this server cluster exhibits "dead front" + * behavior when the "OnOff" attribute is FALSE (Off). This "dead front" behavior includes: + * • clusters other than this cluster that are also exposed may respond with failures to Invoke and Write + * interactions. Such failure responses when in a "dead front" shall be with an INVALID_IN_STATE + * status code. + * • clusters other than this cluster may change the values of their attributes to best-effort values, due to + * the actual values not being defined or available in this state. Device type specifications that require + * support for the DF feature SHOULD define what these best-effort values are. + * • Report Transactions shall continue to be generated. Such transactions may include best-effort values as + * noted above. + * • Event generation logic for clusters other than this cluster is unchanged (noting possible use of + * best-effort attribute values as in the preceding bullets). + * When this feature is supported and the OnOff attribute changes from TRUE to FALSE (e.g. when receiving an Off + * Command, or due to a manual interaction on the device), it shall start executing this "dead front" + * behavior. + * When this feature is supported and the OnOff attribute changes from FALSE to TRUE (e.g. when receiving an On + * Command, or due to a manual interaction on the device), it shall stop executing this "dead front" + * behavior. + * When this feature is supported, and any change of the "dead front" state leads to changes in + * attributes of other clusters due to the "dead front" feature, these attribute changes shall NOT be + * skipped or omitted from the usual processing associated with attribute changes. For example, if an attribute + * changes from value 4 to null on "dead front" behavior due to an Off command being received, this + * change shall be processed for reporting and subscriptions. + */ + public boolean deadFrontBehavior; + /** + * + * When this feature is supported, the Off command shall be supported and the On and Toggle commands shall NOT + * be supported. + * This feature is useful for devices which can be turned off via the Off command received by an instance of + * this cluster but cannot be turned on via commands received by an instance of this cluster due to regulatory + * requirements. + */ + public boolean offOnly; + + public FeatureMap(boolean lighting, boolean deadFrontBehavior, boolean offOnly) { + this.lighting = lighting; + this.deadFrontBehavior = deadFrontBehavior; + this.offOnly = offOnly; + } + } + + public OnOffCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 6, "OnOff"); + } + + protected OnOffCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand off() { + return new ClusterCommand("off"); + } + + public static ClusterCommand on() { + return new ClusterCommand("on"); + } + + public static ClusterCommand toggle() { + return new ClusterCommand("toggle"); + } + + /** + * The OffWithEffect command allows devices to be turned off using enhanced ways of fading. + */ + public static ClusterCommand offWithEffect(EffectIdentifierEnum effectIdentifier, Integer effectVariant) { + Map map = new LinkedHashMap<>(); + if (effectIdentifier != null) { + map.put("effectIdentifier", effectIdentifier); + } + if (effectVariant != null) { + map.put("effectVariant", effectVariant); + } + return new ClusterCommand("offWithEffect", map); + } + + /** + * This command allows the recall of the settings when the device was turned off. + */ + public static ClusterCommand onWithRecallGlobalScene() { + return new ClusterCommand("onWithRecallGlobalScene"); + } + + /** + * This command allows devices to be turned on for a specific duration with a guarded off duration so that SHOULD + * the device be subsequently turned off, further OnWithTimedOff commands, received during this time, are prevented + * from turning the devices back on. Further OnWithTimedOff commands received while the server is turned on, will + * update the period that the device is turned on. + */ + public static ClusterCommand onWithTimedOff(OnOffControlBitmap onOffControl, Integer onTime, Integer offWaitTime) { + Map map = new LinkedHashMap<>(); + if (onOffControl != null) { + map.put("onOffControl", onOffControl); + } + if (onTime != null) { + map.put("onTime", onTime); + } + if (offWaitTime != null) { + map.put("offWaitTime", offWaitTime); + } + return new ClusterCommand("onWithTimedOff", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "onOff : " + onOff + "\n"; + str += "globalSceneControl : " + globalSceneControl + "\n"; + str += "onTime : " + onTime + "\n"; + str += "offWaitTime : " + offWaitTime + "\n"; + str += "startUpOnOff : " + startUpOnOff + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OperationalCredentialsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OperationalCredentialsCluster.java new file mode 100644 index 00000000000..c9a096c31ca --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OperationalCredentialsCluster.java @@ -0,0 +1,458 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * OperationalCredentials + * + * @author Dan Cunningham - Initial contribution + */ +public class OperationalCredentialsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x003E; + public static final String CLUSTER_NAME = "OperationalCredentials"; + public static final String CLUSTER_PREFIX = "operationalCredentials"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_NOCS = "nocs"; + public static final String ATTRIBUTE_FABRICS = "fabrics"; + public static final String ATTRIBUTE_SUPPORTED_FABRICS = "supportedFabrics"; + public static final String ATTRIBUTE_COMMISSIONED_FABRICS = "commissionedFabrics"; + public static final String ATTRIBUTE_TRUSTED_ROOT_CERTIFICATES = "trustedRootCertificates"; + public static final String ATTRIBUTE_CURRENT_FABRIC_INDEX = "currentFabricIndex"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This attribute contains all NOCs applicable to this Node, encoded as a read-only list of NOCStruct. + * Operational Certificates shall be added through the AddNOC command, and shall be removed through the RemoveFabric + * command. + * Upon Factory Data Reset, this attribute shall be set to a default value of an empty list. + * The number of entries in this list shall match the number of entries in the Fabrics attribute. + */ + public List nocs; // 0 list R F A + /** + * This attribute describes all fabrics to which this Node is commissioned, encoded as a read-only list of + * FabricDescriptorStruct. This information may be computed directly from the NOCs attribute. + * Upon Factory Data Reset, this attribute shall be set to a default value of an empty list. + * The number of entries in this list shall match the number of entries in the NOCs attribute. + */ + public List fabrics; // 1 list R F V + /** + * This attribute contains the number of Fabrics that are supported by the device. This value is fixed for a + * particular device. + */ + public Integer supportedFabrics; // 2 uint8 R V + /** + * This attribute contains the number of Fabrics to which the device is currently commissioned. This attribute shall + * be equal to the following: + * • The number of entries in the NOCs attribute. + * • The number of entries in the Fabrics attribute. + * Upon Factory Data Reset, this attribute shall be set to a default value of 0. + */ + public Integer commissionedFabrics; // 3 uint8 R V + /** + * This attribute shall contain a read-only list of Trusted Root CA Certificates (RCAC) installed on the Node, as + * octet strings containing their Matter Certificate Encoding representation. + * These certificates are installed through the AddTrustedRootCertificate command. + * Depending on the method of storage employed by the server, either shared storage for identical root certificates + * shared by many fabrics, or individually stored root certificate per fabric, multiple identical root certificates + * may legally appear within the list. + * To match a root with a given fabric, the root certificate’s subject and subject public key need to be + * cross-referenced with the NOC or ICAC certificates that appear in the NOCs attribute for a given fabric. + * Upon Factory Data Reset, this attribute shall be set to a default value whereby the list is empty. + */ + public List trustedRootCertificates; // 4 list R V + /** + * This attribute shall contain accessing fabric index. + * This attribute is useful to contextualize Fabric-Scoped entries obtained from response commands or attribute + * reads, since a given Fabric may be referenced by a different Fabric Index locally on a remote Node. + */ + public Integer currentFabricIndex; // 5 fabric-idx R V + // Structs + + /** + * This encodes a fabric sensitive NOC chain, underpinning a commissioned Operational Identity for a given Node. + * Note that the Trusted Root CA Certificate is not included in this structure. The roots are available in the + * TrustedRootCertificates attribute of the Node Operational Credentials cluster. + */ + public class NOCStruct { + /** + * This field shall contain the NOC for the struct’s associated fabric, encoded using Matter Certificate + * Encoding. + */ + public OctetString noc; // octstr + /** + * This field shall contain the ICAC or the struct’s associated fabric, encoded using Matter Certificate + * Encoding. If no ICAC is present in the chain, this field shall be set to null. + */ + public OctetString icac; // octstr + public Integer fabricIndex; // FabricIndex + + public NOCStruct(OctetString noc, OctetString icac, Integer fabricIndex) { + this.noc = noc; + this.icac = icac; + this.fabricIndex = fabricIndex; + } + } + + /** + * This structure encodes a Fabric Reference for a fabric within which a given Node is currently commissioned. + */ + public class FabricDescriptorStruct { + /** + * This field shall contain the public key for the trusted root that scopes the fabric referenced by FabricIndex + * and its associated operational credential (see Section 6.4.5.3, “Trusted Root CA Certificates”). The format + * for the key shall be the same as that used in the ec-pub-key field of the Matter Certificate Encoding for the + * root in the operational certificate chain. + */ + public OctetString rootPublicKey; // octstr + /** + * This field shall contain the value of AdminVendorID provided in the AddNOC command that led to the creation + * of this FabricDescriptorStruct. The set of allowed values is defined in AdminVendorID. + * The intent is to provide some measure of user transparency about which entities have Administer privileges on + * the Node. + * Clients shall consider the VendorID field value to be untrustworthy until the NOC chain associated with the + * fabric has passed the Vendor ID Validation Procedure against the associated RCAC. + */ + public Integer vendorId; // vendor-id + /** + * This field shall contain the FabricID allocated to the fabric referenced by FabricIndex. This field shall + * match the value found in the matter-fabric-id field from the operational certificate providing the + * operational identity under this Fabric. + */ + public BigInteger fabricId; // fabric-id + /** + * This field shall contain the NodeID in use within the fabric referenced by FabricIndex. This field shall + * match the value found in the matter-node-id field from the operational certificate providing this operational + * identity. + */ + public BigInteger nodeId; // node-id + /** + * This field shall contain a commissioner-set label for the fabric referenced by FabricIndex. This label is set + * by the UpdateFabricLabel command. + */ + public String label; // string + public Integer fabricIndex; // FabricIndex + + public FabricDescriptorStruct(OctetString rootPublicKey, Integer vendorId, BigInteger fabricId, + BigInteger nodeId, String label, Integer fabricIndex) { + this.rootPublicKey = rootPublicKey; + this.vendorId = vendorId; + this.fabricId = fabricId; + this.nodeId = nodeId; + this.label = label; + this.fabricIndex = fabricIndex; + } + } + + // Enums + /** + * This enumeration is used by the CertificateChainRequest command to convey which certificate from the device + * attestation certificate chain to transmit back to the client. + */ + public enum CertificateChainTypeEnum implements MatterEnum { + DAC_CERTIFICATE(1, "Dac Certificate"), + PAI_CERTIFICATE(2, "Pai Certificate"); + + public final Integer value; + public final String label; + + private CertificateChainTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This enumeration is used by the NOCResponse common response command to convey detailed outcome of several of this + * cluster’s operations. + */ + public enum NodeOperationalCertStatusEnum implements MatterEnum { + OK(0, "Ok"), + INVALID_PUBLIC_KEY(1, "Invalid Public Key"), + INVALID_NODE_OP_ID(2, "Invalid Node Op Id"), + INVALID_NOC(3, "Invalid Noc"), + MISSING_CSR(4, "Missing Csr"), + TABLE_FULL(5, "Table Full"), + INVALID_ADMIN_SUBJECT(6, "Invalid Admin Subject"), + FABRIC_CONFLICT(9, "Fabric Conflict"), + LABEL_CONFLICT(10, "Label Conflict"), + INVALID_FABRIC_INDEX(11, "Invalid Fabric Index"); + + public final Integer value; + public final String label; + + private NodeOperationalCertStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public OperationalCredentialsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 62, "OperationalCredentials"); + } + + protected OperationalCredentialsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall be generated to request the Attestation Information, in the form of an AttestationResponse + * Command. If the AttestationNonce that is provided in the command is malformed, a recipient shall fail the command + * with a Status Code of INVALID_COMMAND. The AttestationNonce field shall be used in the computation of the + * Attestation Information. + */ + public static ClusterCommand attestationRequest(OctetString attestationNonce) { + Map map = new LinkedHashMap<>(); + if (attestationNonce != null) { + map.put("attestationNonce", attestationNonce); + } + return new ClusterCommand("attestationRequest", map); + } + + /** + * If the CertificateType is not a valid value per CertificateChainTypeEnum then the command shall fail with a + * Status Code of INVALID_COMMAND. + */ + public static ClusterCommand certificateChainRequest(CertificateChainTypeEnum certificateType) { + Map map = new LinkedHashMap<>(); + if (certificateType != null) { + map.put("certificateType", certificateType); + } + return new ClusterCommand("certificateChainRequest", map); + } + + /** + * This command shall be generated to execute the Node Operational CSR Procedure and subsequently return the NOCSR + * Information, in the form of a CSRResponse Command. + * The CSRNonce field shall be used in the computation of the NOCSR Information. If the CSRNonce is malformed, then + * this command shall fail with an INVALID_COMMAND status code. + * If the IsForUpdateNOC field is present and set to true, but the command was received over a PASE session, the + * command shall fail with an INVALID_COMMAND status code, as it would never be possible to use a resulting + * subsequent certificate issued from the CSR with the UpdateNOC command, which is forbidden over PASE sessions. + * If the IsForUpdateNOC field is present and set to true, the internal state of the CSR associated keypair shall be + * tagged as being for a subsequent UpdateNOC, otherwise the internal state of the CSR shall be tagged as being for + * a subsequent AddNOC. See AddNOC and UpdateNOC for details about the processing. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * If a prior UpdateNOC or AddNOC command was successfully executed within the fail-safe timer period, then this + * command shall fail with a CONSTRAINT_ERROR status code sent back to the initiator. + * If the Node Operational Key Pair generated during processing of the Node Operational CSR Procedure is found to + * collide with an existing key pair already previously generated and installed, and that check had been executed, + * then this command shall fail with a FAILURE status code sent back to the initiator. + */ + public static ClusterCommand csrRequest(OctetString csrNonce, Boolean isForUpdateNoc) { + Map map = new LinkedHashMap<>(); + if (csrNonce != null) { + map.put("csrNonce", csrNonce); + } + if (isForUpdateNoc != null) { + map.put("isForUpdateNoc", isForUpdateNoc); + } + return new ClusterCommand("csrRequest", map); + } + + /** + * This command shall add a new NOC chain to the device and commission a new Fabric association upon successful + * validation of all arguments and preconditions. + * The new value shall immediately be reflected in the NOCs list attribute. + * A Commissioner or Administrator shall issue this command after issuing the CSRRequest command and receiving its + * response. + * A Commissioner or Administrator SHOULD issue this command after performing the Attestation Procedure. + */ + public static ClusterCommand addNoc(OctetString nocValue, OctetString icacValue, OctetString ipkValue, + BigInteger caseAdminSubject, Integer adminVendorId) { + Map map = new LinkedHashMap<>(); + if (nocValue != null) { + map.put("nocValue", nocValue); + } + if (icacValue != null) { + map.put("icacValue", icacValue); + } + if (ipkValue != null) { + map.put("ipkValue", ipkValue); + } + if (caseAdminSubject != null) { + map.put("caseAdminSubject", caseAdminSubject); + } + if (adminVendorId != null) { + map.put("adminVendorId", adminVendorId); + } + return new ClusterCommand("addNoc", map); + } + + /** + * This command shall replace the NOC and optional associated ICAC (if present) scoped under the accessing fabric + * upon successful validation of all arguments and preconditions. The new value shall immediately be reflected in + * the NOCs list attribute. + * A Commissioner or Administrator shall issue this command after issuing the CSRRequest Command and receiving its + * response. + * A Commissioner or Administrator SHOULD issue this command after performing the Attestation Procedure. + * ### Effect When Received + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * If a prior UpdateNOC or AddNOC command was successfully executed within the fail-safe timer period, then this + * command shall fail with a CONSTRAINT_ERROR status code sent back to the initiator. + * If a prior AddTrustedRootCertificate command was successfully invoked within the fail-safe timer period, then + * this command shall fail with a CONSTRAINT_ERROR status code sent back to the initiator, since the only valid + * following logical operation is invoking the AddNOC command. + * If the prior CSRRequest state that preceded UpdateNOC had the IsForUpdateNOC field indicated as false, then this + * command shall fail with a CONSTRAINT_ERROR status code sent back to the initiator. + * If any of the following conditions arise, the Node shall process an error by responding with an NOCResponse with + * a StatusCode of InvalidNOC as described in Section 11.18.6.7.2, “Handling Errors”: + * • The NOC provided in the NOCValue does not refer in its subject to the FabricID associated with the accessing + * fabric. + * • The ICAC provided in the ICACValue (if present) has a FabricID in its subject that does not match the FabricID + * associated with the accessing fabric. + * Otherwise, the command is considered an update of existing credentials for a given Fabric, and the following + * shall apply: + * 1. The Operational Certificate under the accessing fabric index in the NOCs list shall be updated to match the + * incoming NOCValue and ICACValue (if present), such that the Node’s Operational Identifier within the Fabric + * immediately changes. + * a. The operational key pair associated with the incoming NOC from the NOCValue, and generated by the prior + * CSRRequest command, shall be committed to permanent storage, for subsequent use during CASE. + * b. The operational discovery service record shall immediately reflect the new Operational Identifier. + * c. All internal data reflecting the prior operational identifier of the Node within the Fabric shall be revoked + * and removed, to an outcome equivalent to the disappearance of the prior Node, except for the ongoing CASE session + * context, which shall temporarily remain valid until the NOCResponse has been successfully delivered or until the + * next transport-layer error, so that the response can be received by the Administrator invoking the command. + * Thereafter, the Node shall respond with an NOCResponse with a StatusCode of OK and a FabricIndex field matching + * the FabricIndex under which the updated NOC is scoped. + */ + public static ClusterCommand updateNoc(OctetString nocValue, OctetString icacValue, Integer fabricIndex) { + Map map = new LinkedHashMap<>(); + if (nocValue != null) { + map.put("nocValue", nocValue); + } + if (icacValue != null) { + map.put("icacValue", icacValue); + } + if (fabricIndex != null) { + map.put("fabricIndex", fabricIndex); + } + return new ClusterCommand("updateNoc", map); + } + + /** + * This command shall be used by an Administrator to set the user-visible Label field for a given Fabric, as + * reflected by entries in the Fabrics attribute. An Administrator shall use this command to set the Label to a + * string (possibly selected by the user themselves) that the user can recognize and relate to this Administrator + * • during the commissioning process, and + * • whenever the user chooses to update this string. + * The Label field, along with the VendorID field in the same entry of the Fabrics attribute, SHOULD be used by + * Administrators to provide additional per-fabric context when operations such as RemoveFabric are considered or + * used. + */ + public static ClusterCommand updateFabricLabel(String label, Integer fabricIndex) { + Map map = new LinkedHashMap<>(); + if (label != null) { + map.put("label", label); + } + if (fabricIndex != null) { + map.put("fabricIndex", fabricIndex); + } + return new ClusterCommand("updateFabricLabel", map); + } + + /** + * This command is used by Administrators to remove a given Fabric and delete all associated fabric-scoped data. + * If the given Fabric being removed is the last one to reference a given Trusted Root CA Certificate stored in the + * Trusted Root Certificates list, then that Trusted Root Certificate shall be removed. + * This command, if referring to an already existing Fabric not under the control of the invoking Administrator, + * shall ONLY be invoked after obtaining some form of explicit user consent through some method executed by the + * Administrator or Commissioner. This method of obtaining consent SHOULD employ as much data as possible about the + * existing Fabric associations within the Fabrics list, so that likelihood is as small as possible of a user + * removing a Fabric unwittingly. If a method exists for an Administrator or Commissioner to convey Fabric Removal + * to an entity related to that Fabric, whether in-band or out-of-band, then this method SHOULD be used to notify + * the other Administrative Domain’s party of the removal. Otherwise, users may only observe the removal of a Fabric + * association as persistently failing attempts to reach a Node operationally. + */ + public static ClusterCommand removeFabric(Integer fabricIndex) { + Map map = new LinkedHashMap<>(); + if (fabricIndex != null) { + map.put("fabricIndex", fabricIndex); + } + return new ClusterCommand("removeFabric", map); + } + + /** + * This command shall add a Trusted Root CA Certificate, provided as its Matter Certificate Encoding representation, + * to the TrustedRootCertificates Attribute list and shall ensure the next AddNOC command executed uses the provided + * certificate as its root of trust. + * If the certificate from the RootCACertificate field is already installed, based on exact byte-for-byte equality, + * then this command shall succeed with no change to the list. + * If this command is received without an armed fail-safe context (see ArmFailSafe), then this command shall fail + * with a FAILSAFE_REQUIRED status code sent back to the initiator. + * If a prior AddTrustedRootCertificate command was successfully invoked within the fail-safe timer period, which + * would cause the new invocation to add a second root certificate within a given fail-safe timer period, then this + * command shall fail with a CONSTRAINT_ERROR status code sent back to the initiator. + * If a prior UpdateNOC or AddNOC command was successfully executed within the fail-safe timer period, then this + * command shall fail with a CONSTRAINT_ERROR status code sent back to the initiator. + * If the certificate from the RootCACertificate field fails any validity checks, not fulfilling all the + * requirements for a valid Matter Certificate Encoding representation, including a truncated or oversize value, + * then this command shall fail with an INVALID_COMMAND status code sent back to the initiator. + * Note that the only method of removing a trusted root is by removing the Fabric that uses it as its root of trust + * using the RemoveFabric command. + */ + public static ClusterCommand addTrustedRootCertificate(OctetString rootCaCertificate) { + Map map = new LinkedHashMap<>(); + if (rootCaCertificate != null) { + map.put("rootCaCertificate", rootCaCertificate); + } + return new ClusterCommand("addTrustedRootCertificate", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "nocs : " + nocs + "\n"; + str += "fabrics : " + fabrics + "\n"; + str += "supportedFabrics : " + supportedFabrics + "\n"; + str += "commissionedFabrics : " + commissionedFabrics + "\n"; + str += "trustedRootCertificates : " + trustedRootCertificates + "\n"; + str += "currentFabricIndex : " + currentFabricIndex + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OperationalStateCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OperationalStateCluster.java new file mode 100644 index 00000000000..55d8d9e38e4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OperationalStateCluster.java @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * OperationalState + * + * @author Dan Cunningham - Initial contribution + */ +public class OperationalStateCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0060; + public static final String CLUSTER_NAME = "OperationalState"; + public static final String CLUSTER_PREFIX = "operationalState"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_PHASE_LIST = "phaseList"; + public static final String ATTRIBUTE_CURRENT_PHASE = "currentPhase"; + public static final String ATTRIBUTE_COUNTDOWN_TIME = "countdownTime"; + public static final String ATTRIBUTE_OPERATIONAL_STATE_LIST = "operationalStateList"; + public static final String ATTRIBUTE_OPERATIONAL_STATE = "operationalState"; + public static final String ATTRIBUTE_OPERATIONAL_ERROR = "operationalError"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates a list of names of different phases that the device can go through for the selected function or mode. + * The list may not be in sequence order. For example in a washing machine this could include items such as + * "pre-soak", "rinse", and "spin". These phases are manufacturer specific and may + * change when a different function or mode is selected. + * A null value indicates that the device does not present phases during its operation. When this attribute’s value + * is null, the CurrentPhase attribute shall also be set to null. + */ + public List phaseList; // 0 list R V + /** + * This attribute represents the current phase of operation being performed by the server. This shall be the + * positional index representing the value from the set provided in the PhaseList Attribute, where the first item in + * that list is an index of 0. Thus, this attribute shall have a maximum value that is "length(PhaseList) - + * 1". + * Null if the PhaseList attribute is null or if the PhaseList attribute is an empty list. + */ + public Integer currentPhase; // 1 uint8 R V + /** + * Indicates the estimated time left before the operation is completed, in seconds. + * A value of 0 (zero) means that the operation has completed. + * A value of null represents that there is no time currently defined until operation completion. This may happen, + * for example, because no operation is in progress or because the completion time is unknown. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • If it has changed due to a change in the CurrentPhase or OperationalState attributes, or + * • When it changes from 0 to any other value and vice versa, or + * • When it changes from null to any other value and vice versa, or + * • When it increases, or + * • When there is any increase or decrease in the estimated time remaining that was due to progressing insight of + * the server’s control logic, or + * • When it changes at a rate significantly different from one unit per second. + * Changes to this attribute merely due to the normal passage of time with no other dynamic change of device state + * shall NOT be reported. + * As this attribute is not being reported during a regular countdown, clients SHOULD NOT rely on the reporting of + * this attribute in order to keep track of the remaining duration. + */ + public Integer countdownTime; // 2 elapsed-s R V + /** + * This attribute describes the set of possible operational states that the device exposes. An operational state is + * a fundamental device state such as Running or Error. Details of the phase of a device when, for example, in a + * state of Running are provided by the CurrentPhase attribute. + * All devices shall, at a minimum, expose the set of states matching the commands that are also supported by the + * cluster instance, in addition to Error. The set of possible device states are defined in the + * OperationalStateEnum. A device type requiring implementation of this cluster shall define the set of states that + * are applicable to that specific device type. + */ + public List operationalStateList; // 3 list R V + /** + * This attribute specifies the current operational state of a device. This shall be populated with a valid + * OperationalStateID from the set of values in the OperationalStateList Attribute. + */ + public OperationalStateEnum operationalState; // 4 OperationalStateEnum R V + /** + * This attribute shall specify the details of any current error condition being experienced on the device when the + * OperationalState attribute is populated with Error. Please see ErrorStateStruct for general requirements on the + * population of this attribute. + * When there is no error detected, this shall have an ErrorStateID of NoError. + */ + public ErrorStateStruct operationalError; // 5 ErrorStateStruct R V + // Structs + + /** + * This event is generated when a reportable error condition is detected. A device that generates this event shall + * also set the OperationalState attribute to Error, indicating an error condition. + * This event shall contain the following fields: + */ + public class OperationalError { + public ErrorStateStruct errorState; // ErrorStateStruct + + public OperationalError(ErrorStateStruct errorState) { + this.errorState = errorState; + } + } + + /** + * This event SHOULD be generated when the overall operation ends, successfully or otherwise. For example, the + * completion of a cleaning operation in a Robot Vacuum Cleaner, or the completion of a wash cycle in a Washing + * Machine. + * It is highly recommended that appliances device types employing the Operational State cluster support this event, + * even if it is optional. This assists clients in executing automations or issuing notifications at critical points + * in the device operation cycles. + * This event shall contain the following fields: + */ + public class OperationCompletion { + /** + * This field provides an indication of the state at the end of the operation. This field shall have a value + * from the ErrorStateEnum set. A value of NoError indicates success, that is, no error has been detected. + */ + public Integer completionErrorCode; // enum8 + /** + * The total operational time, in seconds, from when the operation was started via an initial Start command or + * autonomous/manual starting action, until the operation completed. This includes any time spent while paused. + * There may be cases whereby the total operational time exceeds the maximum value that can be conveyed by this + * attribute, in such instances, this attribute shall be populated with null. + */ + public Integer totalOperationalTime; // elapsed-s + /** + * The total time spent in the paused state, in seconds. There may be cases whereby the total paused time + * exceeds the maximum value that can be conveyed by this attribute, in such instances, this attribute shall be + * populated with null. + */ + public Integer pausedTime; // elapsed-s + + public OperationCompletion(Integer completionErrorCode, Integer totalOperationalTime, Integer pausedTime) { + this.completionErrorCode = completionErrorCode; + this.totalOperationalTime = totalOperationalTime; + this.pausedTime = pausedTime; + } + } + + /** + * The OperationalStateStruct is used to indicate a possible state of the device. + */ + public class OperationalStateStruct { + /** + * This shall be populated with a value from the OperationalStateEnum. + */ + public OperationalStateEnum operationalStateId; // OperationalStateEnum + /** + * This field shall be present if the OperationalStateID is from the set reserved for Manufacturer Specific + * States, otherwise it shall NOT be present. If present, this shall contain a human-readable description of the + * operational state. + */ + public String operationalStateLabel; // string + + public OperationalStateStruct(OperationalStateEnum operationalStateId, String operationalStateLabel) { + this.operationalStateId = operationalStateId; + this.operationalStateLabel = operationalStateLabel; + } + } + + public class ErrorStateStruct { + /** + * This shall be populated with a value from the ErrorStateEnum. + */ + public ErrorStateEnum errorStateId; // ErrorStateEnum + /** + * This field shall be present if the ErrorStateID is from the set reserved for Manufacturer Specific Errors, + * otherwise it shall NOT be present. If present, this shall contain a human-readable description of the + * ErrorStateID; e.g. for a manufacturer specific ErrorStateID of "0x80" the ErrorStateLabel may + * contain "My special error". + */ + public String errorStateLabel; // string + /** + * This shall be a human-readable string that provides details about the error condition. As an example, if the + * ErrorStateID indicates that the device is a Robotic Vacuum that is stuck, the ErrorStateDetails contains + * "left wheel blocked". + */ + public String errorStateDetails; // string + + public ErrorStateStruct(ErrorStateEnum errorStateId, String errorStateLabel, String errorStateDetails) { + this.errorStateId = errorStateId; + this.errorStateLabel = errorStateLabel; + this.errorStateDetails = errorStateDetails; + } + } + + // Enums + /** + * This type defines the set of known operational state values, and is derived from enum8. The following table + * defines the applicable ranges for values that are defined within this type. All values that are undefined shall + * be treated as reserved. As shown by the table, states that may be specific to a certain Device Type or other + * modality shall be defined in a derived cluster of this cluster. + * The derived cluster-specific state definitions shall NOT duplicate any general state definitions. That is, a + * derived cluster specification of this cluster cannot define states with the same semantics as the general states + * defined below. + * A manufacturer-specific state definition shall NOT duplicate the general state definitions or derived cluster + * state definitions. That is, a manufacturer-defined state defined for this cluster or a derived cluster thereof + * cannot define a state with the same semantics as the general states defined below or states defined in a derived + * cluster. Such manufacturer-specific state definitions shall be scoped in the context of the Vendor ID present in + * the Basic Information cluster. + * The following table defines the generally applicable states. + */ + public enum OperationalStateEnum implements MatterEnum { + STOPPED(0, "Stopped"), + RUNNING(1, "Running"), + PAUSED(2, "Paused"), + ERROR(3, "Error"); + + public final Integer value; + public final String label; + + private OperationalStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * This type defines the set of known operational error values, and is derived from enum8. The following table + * defines the applicable ranges for values that are defined within this type. All values that are undefined shall + * be treated as reserved. As shown by the table, errors that may be specific to a certain Device Type or other + * modality shall be defined in a derived cluster of this cluster. + * The derived cluster-specific error definitions shall NOT duplicate the general error definitions. That is, a + * derived cluster specification of this cluster cannot define errors with the same semantics as the general errors + * defined below. + * The manufacturer-specific error definitions shall NOT duplicate the general error definitions or derived + * cluster-specific error definitions. That is, a manufacturer-defined error defined for this cluster or a derived + * cluster thereof cannot define errors with the same semantics as the general errors defined below or errors + * defined in a derived cluster. Such manufacturer-specific error definitions shall be scoped in the context of the + * Vendor ID present in the Basic Information cluster. + * The set of ErrorStateID field values defined in each of the generic or derived Operational State cluster + * specifications is called ErrorState. + */ + public enum ErrorStateEnum implements MatterEnum { + NO_ERROR(0, "No Error"), + UNABLE_TO_START_OR_RESUME(1, "Unable To Start Or Resume"), + UNABLE_TO_COMPLETE_OPERATION(2, "Unable To Complete Operation"), + COMMAND_INVALID_IN_STATE(3, "Command Invalid In State"); + + public final Integer value; + public final String label; + + private ErrorStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public OperationalStateCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 96, "OperationalState"); + } + + protected OperationalStateCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall be supported if the device supports remotely pausing the operation. If this command is + * supported, the Resume command shall also be supported. + * On receipt of this command, the device shall pause its operation if it is possible based on the current function + * of the server. For example, if it is at a point where it is safe to do so and/or permitted, but can be restarted + * from the point at which pause occurred. + * If this command is received when already in the Paused state the device shall respond with an + * OperationalCommandResponse command with an ErrorStateID of NoError but take no further action. + * A device that receives this command in any state which is not Pause-compatible shall respond with an + * OperationalCommandResponse command with an ErrorStateID of CommandInvalidInState and shall take no further + * action. + * States are defined as Pause-compatible as follows: + * • For states defined in this cluster specification, in Table 3, “Pause Compatibility”. + * • For states defined by derived cluster specifications, in the corresponding specifications. + * • For manufacturer-specific states, by the manufacturer. + * A device that is unable to honor the Pause command for whatever reason shall respond with an + * OperationalCommandResponse command with an ErrorStateID of CommandInvalidInState but take no further action. + * Otherwise, on success: + * • The OperationalState attribute shall be set to Paused. + * • The device shall respond with an OperationalCommandResponse command with an ErrorStateID of NoError. + * The following table defines the compatibility of this cluster’s states with the Pause command. + * ### Table 3. Pause Compatibility + */ + public static ClusterCommand pause() { + return new ClusterCommand("pause"); + } + + /** + * This command shall be supported if the device supports remotely stopping the operation. + * On receipt of this command, the device shall stop its operation if it is at a position where it is safe to do so + * and/or permitted. Restart of the device following the receipt of the Stop command shall require attended + * operation unless remote start is allowed by the device type and any jurisdiction governing remote operation of + * the device. + * If this command is received when already in the Stopped state the device shall respond with an + * OperationalCommandResponse command with an ErrorStateID of NoError but take no further action. + * A device that is unable to honor the Stop command for whatever reason shall respond with an + * OperationalCommandResponse command with an ErrorStateID of CommandInvalidInState but take no further action. + * Otherwise, on success: + * • The OperationalState attribute shall be set to Stopped. + * • The device shall respond with an OperationalCommandResponse command with an ErrorStateID of NoError. + */ + public static ClusterCommand stop() { + return new ClusterCommand("stop"); + } + + /** + * This command shall be supported if the device supports remotely starting the operation. If this command is + * supported, the 'Stop command shall also be supported. + * On receipt of this command, the device shall start its operation if it is safe to do so and the device is in an + * operational state from which it can be started. There may be either regulatory or manufacturer-imposed safety and + * security requirements that first necessitate some specific action at the device before a Start command can be + * honored. In such instances, a device shall respond with a status code of CommandInvalidInState if a Start command + * is received prior to the required on-device action. + * If this command is received when already in the Running state the device shall respond with an + * OperationalCommandResponse command with an ErrorStateID of NoError but take no further action. + * A device that is unable to honor the Start command for whatever reason shall respond with an + * OperationalCommandResponse command with an ErrorStateID of UnableToStartOrResume but take no further action. + * Otherwise, on success: + * • The OperationalState attribute shall be set to Running. + * • The device shall respond with an OperationalCommandResponse command with an ErrorStateID of NoError. + */ + public static ClusterCommand start() { + return new ClusterCommand("start"); + } + + /** + * This command shall be supported if the device supports remotely resuming the operation. If this command is + * supported, the Pause command shall also be supported. + * On receipt of this command, the device shall resume its operation from the point it was at when it received the + * Pause command, or from the point when it was paused by means outside of this cluster (for example by manual + * button press). + * If this command is received when already in the Running state the device shall respond with an + * OperationalCommandResponse command with an ErrorStateID of NoError but take no further action. + * A device that receives this command in any state which is not Resume-compatible shall respond with an + * OperationalCommandResponse command with an ErrorStateID of CommandInvalidInState and shall take no further + * action. + * States are defined as Resume-compatible as follows: + * • For states defined in this cluster specification, in Table 4, “Resume Compatibility”. + * • For states defined by derived cluster specifications, in the corresponding specifications. + * • For manufacturer-specific states, by the manufacturer. + * The following table defines the compatibility of this cluster’s states with the Resume command. + * ### Table 4. Resume Compatibility + * A device that is unable to honor the Resume command for any other reason shall respond with an + * OperationalCommandResponse command with an ErrorStateID of UnableToStartOrResume but take no further action. + * Otherwise, on success: + * • The OperationalState attribute shall be set to the most recent non-Error operational state prior to entering + * the Paused state. + * • The device shall respond with an OperationalCommandResponse command with an ErrorStateID of NoError. + */ + public static ClusterCommand resume() { + return new ClusterCommand("resume"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "phaseList : " + phaseList + "\n"; + str += "currentPhase : " + currentPhase + "\n"; + str += "countdownTime : " + countdownTime + "\n"; + str += "operationalStateList : " + operationalStateList + "\n"; + str += "operationalState : " + operationalState + "\n"; + str += "operationalError : " + operationalError + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OtaSoftwareUpdateProviderCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OtaSoftwareUpdateProviderCluster.java new file mode 100644 index 00000000000..62a7635358c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OtaSoftwareUpdateProviderCluster.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * OtaSoftwareUpdateProvider + * + * @author Dan Cunningham - Initial contribution + */ +public class OtaSoftwareUpdateProviderCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0029; + public static final String CLUSTER_NAME = "OtaSoftwareUpdateProvider"; + public static final String CLUSTER_PREFIX = "otaSoftwareUpdateProvider"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + + // Enums + /** + * See Section 11.20.3.2, “Querying the OTA Provider” for the semantics of these values. + */ + public enum StatusEnum implements MatterEnum { + UPDATE_AVAILABLE(0, "Update Available"), + BUSY(1, "Busy"), + NOT_AVAILABLE(2, "Not Available"), + DOWNLOAD_PROTOCOL_NOT_SUPPORTED(3, "Download Protocol Not Supported"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * See Section 11.20.3.6, “Applying a software update” for the semantics of the values. This enumeration is used in + * the Action field of the ApplyUpdateResponse command. See (Action). + */ + public enum ApplyUpdateActionEnum implements MatterEnum { + PROCEED(0, "Proceed"), + AWAIT_NEXT_ACTION(1, "Await Next Action"), + DISCONTINUE(2, "Discontinue"); + + public final Integer value; + public final String label; + + private ApplyUpdateActionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * Note that only HTTP over TLS (HTTPS) is supported (see RFC 7230). Using HTTP without TLS shall NOT be supported, + * as there is no way to authenticate the involved participants. + */ + public enum DownloadProtocolEnum implements MatterEnum { + BDX_SYNCHRONOUS(0, "Bdx Synchronous"), + BDX_ASYNCHRONOUS(1, "Bdx Asynchronous"), + HTTPS(2, "Https"), + VENDOR_SPECIFIC(3, "Vendor Specific"); + + public final Integer value; + public final String label; + + private DownloadProtocolEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public OtaSoftwareUpdateProviderCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 41, "OtaSoftwareUpdateProvider"); + } + + protected OtaSoftwareUpdateProviderCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this command shall trigger an attempt to find an updated Software Image by the OTA Provider to + * match the OTA Requestor’s constraints provided in the payload fields. + */ + public static ClusterCommand queryImage(Integer vendorId, Integer productId, Integer softwareVersion, + List protocolsSupported, Integer hardwareVersion, String location, + Boolean requestorCanConsent, OctetString metadataForProvider) { + Map map = new LinkedHashMap<>(); + if (vendorId != null) { + map.put("vendorId", vendorId); + } + if (productId != null) { + map.put("productId", productId); + } + if (softwareVersion != null) { + map.put("softwareVersion", softwareVersion); + } + if (protocolsSupported != null) { + map.put("protocolsSupported", protocolsSupported); + } + if (hardwareVersion != null) { + map.put("hardwareVersion", hardwareVersion); + } + if (location != null) { + map.put("location", location); + } + if (requestorCanConsent != null) { + map.put("requestorCanConsent", requestorCanConsent); + } + if (metadataForProvider != null) { + map.put("metadataForProvider", metadataForProvider); + } + return new ClusterCommand("queryImage", map); + } + + public static ClusterCommand applyUpdateRequest(OctetString updateToken, Integer newVersion) { + Map map = new LinkedHashMap<>(); + if (updateToken != null) { + map.put("updateToken", updateToken); + } + if (newVersion != null) { + map.put("newVersion", newVersion); + } + return new ClusterCommand("applyUpdateRequest", map); + } + + public static ClusterCommand notifyUpdateApplied(OctetString updateToken, Integer softwareVersion) { + Map map = new LinkedHashMap<>(); + if (updateToken != null) { + map.put("updateToken", updateToken); + } + if (softwareVersion != null) { + map.put("softwareVersion", softwareVersion); + } + return new ClusterCommand("notifyUpdateApplied", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OtaSoftwareUpdateRequestorCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OtaSoftwareUpdateRequestorCluster.java new file mode 100644 index 00000000000..4fdb9b44510 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OtaSoftwareUpdateRequestorCluster.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * OtaSoftwareUpdateRequestor + * + * @author Dan Cunningham - Initial contribution + */ +public class OtaSoftwareUpdateRequestorCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x002A; + public static final String CLUSTER_NAME = "OtaSoftwareUpdateRequestor"; + public static final String CLUSTER_PREFIX = "otaSoftwareUpdateRequestor"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_DEFAULT_OTA_PROVIDERS = "defaultOtaProviders"; + public static final String ATTRIBUTE_UPDATE_POSSIBLE = "updatePossible"; + public static final String ATTRIBUTE_UPDATE_STATE = "updateState"; + public static final String ATTRIBUTE_UPDATE_STATE_PROGRESS = "updateStateProgress"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This field is a list of ProviderLocation whose entries shall be set by Administrators, either during + * Commissioning or at a later time, to set the ProviderLocation for the default OTA Provider Node to use for + * software updates on a given Fabric. + * There shall NOT be more than one entry per Fabric. On a list update that would introduce more than one entry per + * fabric, the write shall fail with CONSTRAINT_ERROR status code. + * Provider Locations obtained using the AnnounceOTAProvider command shall NOT overwrite values set in the + * DefaultOTAProviders attribute. + */ + public List defaultOtaProviders; // 0 list RW F VA + /** + * This field shall be set to True if the OTA Requestor is currently able to be updated. Otherwise, it shall be set + * to False in case of any condition preventing update being possible, such as insufficient capacity of an internal + * battery. This field is merely informational for diagnostics purposes and shall NOT affect the responses provided + * by an OTA Provider to an OTA Requestor. + */ + public Boolean updatePossible; // 1 bool R V + /** + * This field shall reflect the current state of the OTA Requestor with regards to obtaining software updates. See + * Section 11.20.7.4.2, “UpdateStateEnum Type” for possible values. + * This field SHOULD be updated in a timely manner whenever OTA Requestor internal state updates. + */ + public UpdateStateEnum updateState; // 2 UpdateStateEnum R V + /** + * This field shall reflect the percentage value of progress, relative to the current UpdateState, if applicable to + * the state. + * The value of this field shall be null if a progress indication does not apply to the current state. + * A value of 0 shall indicate that the beginning has occurred. A value of 100 shall indicate completion. + * This field may be updated infrequently. Some care SHOULD be taken by Nodes to avoid over-reporting progress when + * this attribute is part of a subscription. + */ + public Integer updateStateProgress; // 3 uint8 R V + // Structs + + /** + * This event shall be generated when a change of the UpdateState attribute occurs due to an OTA Requestor moving + * through the states necessary to query for updates. + */ + public class StateTransition { + /** + * This field shall be set to the state that preceded the transition causing this event to be generated, if such + * a state existed. If no previous state exists, the value shall be Unknown. + */ + public UpdateStateEnum previousState; // UpdateStateEnum + /** + * This field shall be set to the state now in effect through the transition causing this event to be generated. + */ + public UpdateStateEnum newState; // UpdateStateEnum + /** + * This field shall be set to the reason why this event was generated. + */ + public ChangeReasonEnum reason; // ChangeReasonEnum + /** + * This field shall be set to the target SoftwareVersion which is the subject of the operation, whenever the + * NewState is Downloading, Applying or RollingBack. Otherwise TargetSoftwareVersion shall be null. + */ + public Integer targetSoftwareVersion; // uint32 + + public StateTransition(UpdateStateEnum previousState, UpdateStateEnum newState, ChangeReasonEnum reason, + Integer targetSoftwareVersion) { + this.previousState = previousState; + this.newState = newState; + this.reason = reason; + this.targetSoftwareVersion = targetSoftwareVersion; + } + } + + /** + * This event shall be generated whenever a new version starts executing after being applied due to a software + * update. This event SHOULD be generated even if a software update was done using means outside of this cluster. + */ + public class VersionApplied { + /** + * This field shall be set to the same value as the one available in the Software Version attribute of the Basic + * Information Cluster for the newly executing version. + */ + public Integer softwareVersion; // uint32 + /** + * This field shall be set to the ProductID applying to the executing version, as reflected by the Basic + * Information Cluster. This can be used to detect a product updating its definition due to a large-scale + * functional update that may impact aspects of the product reflected in the DeviceModel schema of the + * Distributed Compliance Ledger. + */ + public Integer productId; // uint16 + + public VersionApplied(Integer softwareVersion, Integer productId) { + this.softwareVersion = softwareVersion; + this.productId = productId; + } + } + + /** + * This event shall be generated whenever an error occurs during OTA Requestor download operation. + */ + public class DownloadError { + /** + * This field shall be set to the value of the SoftwareVersion being downloaded, matching the SoftwareVersion + * field of the QueryImageResponse that caused the failing download to take place. + */ + public Integer softwareVersion; // uint32 + /** + * This field shall be set to the number of bytes that have been downloaded during the failing transfer that + * caused this event to be generated. + */ + public BigInteger bytesDownloaded; // uint64 + /** + * This field shall be set to the nearest integer percent value reflecting how far within the transfer the + * failure occurred during the failing transfer that caused this event to be generated, unless the total length + * of the transfer is unknown, in which case it shall be null. + */ + public Integer progressPercent; // uint8 + /** + * This field SHOULD be set to some internal product-specific error code, closest in temporal/functional + * proximity to the failure that caused this event to be generated. Otherwise, it shall be null. This event + * field may be used for debugging purposes and no uniform definition exists related to its meaning. + */ + public BigInteger platformCode; // int64 + + public DownloadError(Integer softwareVersion, BigInteger bytesDownloaded, Integer progressPercent, + BigInteger platformCode) { + this.softwareVersion = softwareVersion; + this.bytesDownloaded = bytesDownloaded; + this.progressPercent = progressPercent; + this.platformCode = platformCode; + } + } + + /** + * This structure encodes a fabric-scoped location of an OTA provider on a given fabric. + */ + public class ProviderLocation { + /** + * This field shall contain the Node ID of the OTA Provider to contact within the Fabric identified by the + * FabricIndex. + */ + public BigInteger providerNodeId; // node-id + /** + * This field shall contain the endpoint number which has the OTA Provider device type and OTA Software Update + * Provider cluster server on the ProviderNodeID. This is provided to avoid having to do discovery of the + * location of that endpoint by walking over all endpoints and checking their Descriptor Cluster. + */ + public Integer endpoint; // endpoint-no + public Integer fabricIndex; // FabricIndex + + public ProviderLocation(BigInteger providerNodeId, Integer endpoint, Integer fabricIndex) { + this.providerNodeId = providerNodeId; + this.endpoint = endpoint; + this.fabricIndex = fabricIndex; + } + } + + // Enums + public enum AnnouncementReasonEnum implements MatterEnum { + SIMPLE_ANNOUNCEMENT(0, "Simple Announcement"), + UPDATE_AVAILABLE(1, "Update Available"), + URGENT_UPDATE_AVAILABLE(2, "Urgent Update Available"); + + public final Integer value; + public final String label; + + private AnnouncementReasonEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum UpdateStateEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + IDLE(1, "Idle"), + QUERYING(2, "Querying"), + DELAYED_ON_QUERY(3, "Delayed On Query"), + DOWNLOADING(4, "Downloading"), + APPLYING(5, "Applying"), + DELAYED_ON_APPLY(6, "Delayed On Apply"), + ROLLING_BACK(7, "Rolling Back"), + DELAYED_ON_USER_CONSENT(8, "Delayed On User Consent"); + + public final Integer value; + public final String label; + + private UpdateStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ChangeReasonEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + SUCCESS(1, "Success"), + FAILURE(2, "Failure"), + TIME_OUT(3, "Time Out"), + DELAY_BY_PROVIDER(4, "Delay By Provider"); + + public final Integer value; + public final String label; + + private ChangeReasonEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public OtaSoftwareUpdateRequestorCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 42, "OtaSoftwareUpdateRequestor"); + } + + protected OtaSoftwareUpdateRequestorCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command may be invoked by Administrators to announce the presence of a particular OTA Provider. + * This command shall be scoped to the accessing fabric. + * If the accessing fabric index is 0, this command shall fail with an UNSUPPORTED_ACCESS status code. + */ + public static ClusterCommand announceOtaProvider(BigInteger providerNodeId, Integer vendorId, + AnnouncementReasonEnum announcementReason, OctetString metadataForNode, Integer endpoint, + Integer fabricIndex) { + Map map = new LinkedHashMap<>(); + if (providerNodeId != null) { + map.put("providerNodeId", providerNodeId); + } + if (vendorId != null) { + map.put("vendorId", vendorId); + } + if (announcementReason != null) { + map.put("announcementReason", announcementReason); + } + if (metadataForNode != null) { + map.put("metadataForNode", metadataForNode); + } + if (endpoint != null) { + map.put("endpoint", endpoint); + } + if (fabricIndex != null) { + map.put("fabricIndex", fabricIndex); + } + return new ClusterCommand("announceOtaProvider", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "defaultOtaProviders : " + defaultOtaProviders + "\n"; + str += "updatePossible : " + updatePossible + "\n"; + str += "updateState : " + updateState + "\n"; + str += "updateStateProgress : " + updateStateProgress + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OvenCavityOperationalStateCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OvenCavityOperationalStateCluster.java new file mode 100644 index 00000000000..c304be7a718 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OvenCavityOperationalStateCluster.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * OvenCavityOperationalState + * + * @author Dan Cunningham - Initial contribution + */ +public class OvenCavityOperationalStateCluster extends OperationalStateCluster { + + public static final int CLUSTER_ID = 0x0048; + public static final String CLUSTER_NAME = "OvenCavityOperationalState"; + public static final String CLUSTER_PREFIX = "ovenCavityOperationalState"; + + public OvenCavityOperationalStateCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 72, "OvenCavityOperationalState"); + } + + protected OvenCavityOperationalStateCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OvenModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OvenModeCluster.java new file mode 100644 index 00000000000..39498a2e2af --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OvenModeCluster.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * OvenMode + * + * @author Dan Cunningham - Initial contribution + */ +public class OvenModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x0049; + public static final String CLUSTER_NAME = "OvenMode"; + public static final String CLUSTER_PREFIX = "ovenMode"; + + // Structs + /** + * The table below lists the changes relative to the Mode Base cluster for the fields of the ModeOptionStruct type. + * A blank field indicates no change. + */ + public class ModeOptionStruct { + public String label; // + public String mode; // + public String modeTags; // + + public ModeOptionStruct(String label, String mode, String modeTags) { + this.label = label; + this.mode = mode; + this.modeTags = modeTags; + } + } + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + BAKE(16384, "Bake"), + CONVECTION(16385, "Convection"), + GRILL(16386, "Grill"), + ROAST(16387, "Roast"), + CLEAN(16388, "Clean"), + CONVECTION_BAKE(16389, "Convection Bake"), + CONVECTION_ROAST(16390, "Convection Roast"), + WARMING(16391, "Warming"), + PROOFING(16392, "Proofing"), + STEAM(16393, "Steam"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public OvenModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 73, "OvenMode"); + } + + protected OvenModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OzoneConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OzoneConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..46e0d145938 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/OzoneConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * OzoneConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class OzoneConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x0415; + public static final String CLUSTER_NAME = "OzoneConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "ozoneConcentrationMeasurement"; + + public OzoneConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1045, "OzoneConcentrationMeasurement"); + } + + protected OzoneConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm10ConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm10ConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..22af5fa73c7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm10ConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Pm10ConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class Pm10ConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x042D; + public static final String CLUSTER_NAME = "Pm10ConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "pm10ConcentrationMeasurement"; + + public Pm10ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1069, "Pm10ConcentrationMeasurement"); + } + + protected Pm10ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm1ConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm1ConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..10f6c4cef8e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm1ConcentrationMeasurementCluster.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Pm1ConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class Pm1ConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x042C; + public static final String CLUSTER_NAME = "Pm1ConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "pm1ConcentrationMeasurement"; + + public Pm1ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1068, "Pm1ConcentrationMeasurement"); + } + + protected Pm1ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm25ConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm25ConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..2fb03f42e26 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/Pm25ConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Pm25ConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class Pm25ConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x042A; + public static final String CLUSTER_NAME = "Pm25ConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "pm25ConcentrationMeasurement"; + + public Pm25ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1066, "Pm25ConcentrationMeasurement"); + } + + protected Pm25ConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerSourceCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerSourceCluster.java new file mode 100644 index 00000000000..22e9b61a922 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerSourceCluster.java @@ -0,0 +1,771 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * PowerSource + * + * @author Dan Cunningham - Initial contribution + */ +public class PowerSourceCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x002F; + public static final String CLUSTER_NAME = "PowerSource"; + public static final String CLUSTER_PREFIX = "powerSource"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_STATUS = "status"; + public static final String ATTRIBUTE_ORDER = "order"; + public static final String ATTRIBUTE_DESCRIPTION = "description"; + public static final String ATTRIBUTE_WIRED_ASSESSED_INPUT_VOLTAGE = "wiredAssessedInputVoltage"; + public static final String ATTRIBUTE_WIRED_ASSESSED_INPUT_FREQUENCY = "wiredAssessedInputFrequency"; + public static final String ATTRIBUTE_WIRED_CURRENT_TYPE = "wiredCurrentType"; + public static final String ATTRIBUTE_WIRED_ASSESSED_CURRENT = "wiredAssessedCurrent"; + public static final String ATTRIBUTE_WIRED_NOMINAL_VOLTAGE = "wiredNominalVoltage"; + public static final String ATTRIBUTE_WIRED_MAXIMUM_CURRENT = "wiredMaximumCurrent"; + public static final String ATTRIBUTE_WIRED_PRESENT = "wiredPresent"; + public static final String ATTRIBUTE_ACTIVE_WIRED_FAULTS = "activeWiredFaults"; + public static final String ATTRIBUTE_BAT_VOLTAGE = "batVoltage"; + public static final String ATTRIBUTE_BAT_PERCENT_REMAINING = "batPercentRemaining"; + public static final String ATTRIBUTE_BAT_TIME_REMAINING = "batTimeRemaining"; + public static final String ATTRIBUTE_BAT_CHARGE_LEVEL = "batChargeLevel"; + public static final String ATTRIBUTE_BAT_REPLACEMENT_NEEDED = "batReplacementNeeded"; + public static final String ATTRIBUTE_BAT_REPLACEABILITY = "batReplaceability"; + public static final String ATTRIBUTE_BAT_PRESENT = "batPresent"; + public static final String ATTRIBUTE_ACTIVE_BAT_FAULTS = "activeBatFaults"; + public static final String ATTRIBUTE_BAT_REPLACEMENT_DESCRIPTION = "batReplacementDescription"; + public static final String ATTRIBUTE_BAT_COMMON_DESIGNATION = "batCommonDesignation"; + public static final String ATTRIBUTE_BAT_ANSI_DESIGNATION = "batAnsiDesignation"; + public static final String ATTRIBUTE_BAT_IEC_DESIGNATION = "batIecDesignation"; + public static final String ATTRIBUTE_BAT_APPROVED_CHEMISTRY = "batApprovedChemistry"; + public static final String ATTRIBUTE_BAT_CAPACITY = "batCapacity"; + public static final String ATTRIBUTE_BAT_QUANTITY = "batQuantity"; + public static final String ATTRIBUTE_BAT_CHARGE_STATE = "batChargeState"; + public static final String ATTRIBUTE_BAT_TIME_TO_FULL_CHARGE = "batTimeToFullCharge"; + public static final String ATTRIBUTE_BAT_FUNCTIONAL_WHILE_CHARGING = "batFunctionalWhileCharging"; + public static final String ATTRIBUTE_BAT_CHARGING_CURRENT = "batChargingCurrent"; + public static final String ATTRIBUTE_ACTIVE_BAT_CHARGE_FAULTS = "activeBatChargeFaults"; + public static final String ATTRIBUTE_ENDPOINT_LIST = "endpointList"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the participation of this power source in providing power to the Node as specified in + * PowerSourceStatusEnum. + */ + public PowerSourceStatusEnum status; // 0 PowerSourceStatusEnum R V + /** + * Indicates the relative preference with which the Node will select this source to provide power. A source with a + * lower order shall be selected by the Node to provide power before any other source with a higher order, if the + * lower order source is available (see Status). + * Note, Order is read-only and therefore NOT intended to allow clients control over power source selection. + */ + public Integer order; // 1 uint8 R V + /** + * This attribute shall provide a user-facing description of this source, used to distinguish it from other power + * sources, e.g. "DC Power", "Primary Battery" or "Battery back-up". This attribute + * shall NOT be used to convey information such as battery form factor, or chemistry. + */ + public String description; // 2 string R V + /** + * Indicates the assessed RMS or DC voltage currently provided by the hard-wired source, in mV (millivolts). A value + * of NULL shall indicate the Node is currently unable to assess the value. If the wired source is not connected, + * but the Node is still able to assess a value, then the assessed value may be reported. + */ + public Integer wiredAssessedInputVoltage; // 3 uint32 R V + /** + * Indicates the assessed frequency of the voltage, currently provided by the hard-wired source, in Hz. A value of + * NULL shall indicate the Node is currently unable to assess the value. If the wired source is not connected, but + * the Node is still able to assess a value, then the assessed value may be reported. + */ + public Integer wiredAssessedInputFrequency; // 4 uint16 R V + /** + * Indicates the type of current the Node expects to be provided by the hard-wired source as specified in + * WiredCurrentTypeEnum. + */ + public WiredCurrentTypeEnum wiredCurrentType; // 5 WiredCurrentTypeEnum R V + /** + * Indicates the assessed instantaneous current draw of the Node on the hard-wired source, in mA (milliamps). A + * value of NULL shall indicate the Node is currently unable to assess the value. If the wired source is not + * connected, but the Node is still able to assess a value, then the assessed value may be reported. + */ + public Integer wiredAssessedCurrent; // 6 uint32 R V + /** + * Indicates the nominal voltage, printed as part of the Node’s regulatory compliance label in mV (millivolts), + * expected to be provided by the hard-wired source. + */ + public Integer wiredNominalVoltage; // 7 uint32 R V + /** + * Indicates the maximum current, printed as part of the Node’s regulatory compliance label in mA (milliamps), + * expected to be provided by the hard-wired source. + */ + public Integer wiredMaximumCurrent; // 8 uint32 R V + /** + * Indicates if the Node detects that the hard-wired power source is properly connected. + */ + public Boolean wiredPresent; // 9 bool R V + /** + * Indicates the set of wired faults currently detected by the Node on this power source. This set is represented as + * a list of WiredFaultEnum. When the Node detects a fault has been raised, the appropriate WiredFaultEnum value + * shall be added to this list, provided it is not already present. This list shall NOT contain more than one + * instance of a specific WiredFaultEnum value. When the Node detects all conditions contributing to a fault have + * been cleared, the corresponding WiredFaultEnum value shall be removed from this list. An empty list shall + * indicate there are currently no active faults. The order of this list SHOULD have no significance. Clients + * interested in monitoring changes in active faults may subscribe to this attribute, or they may subscribe to + * WiredFaultChange. + */ + public List activeWiredFaults; // 10 list R V + /** + * Indicates the currently measured output voltage of the battery in mV (millivolts). A value of NULL shall indicate + * the Node is currently unable to assess the value. + */ + public Integer batVoltage; // 11 uint32 R V + /** + * Indicates the estimated percentage of battery charge remaining until the battery will no longer be able to + * provide power to the Node. Values are expressed in half percent units, ranging from 0 to 200. E.g. a value of 48 + * is equivalent to 24%. A value of NULL shall indicate the Node is currently unable to assess the value. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once every 10 seconds, or + * • When it changes from null to any other value and vice versa. + * Since reporting consumes power, devices SHOULD be careful not to over-report. + */ + public Integer batPercentRemaining; // 12 uint8 R V + /** + * Indicates the estimated time in seconds before the battery will no longer be able to provide power to the Node. A + * value of NULL shall indicate the Node is currently unable to assess the value. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once every 10 seconds, or + * • When it changes from null to any other value and vice versa. + * Since reporting consumes power, devices SHOULD be careful not to over-report. + */ + public Integer batTimeRemaining; // 13 uint32 R V + /** + * Indicates a coarse ranking of the charge level of the battery, used to indicate when intervention is required as + * specified in BatChargeLevelEnum. + */ + public BatChargeLevelEnum batChargeLevel; // 14 BatChargeLevelEnum R V + /** + * Indicates if the battery needs to be replaced. Replacement may be simple routine maintenance, such as with a + * single use, non-rechargeable cell. Replacement, however, may also indicate end of life, or serious fault with a + * rechargeable or even non-replaceable cell. + */ + public Boolean batReplacementNeeded; // 15 bool R V + /** + * This attribute shall indicate the replaceability of the battery as specified in BatReplaceabilityEnum. + */ + public BatReplaceabilityEnum batReplaceability; // 16 BatReplaceabilityEnum R V + /** + * Indicates whether the Node detects that the batteries are properly installed. + */ + public Boolean batPresent; // 17 bool R V + /** + * Indicates the set of battery faults currently detected by the Node on this power source. This set is represented + * as a list of BatFaultEnum. When the Node detects a fault has been raised, the appropriate BatFaultEnum value + * shall be added to this list, provided it is not already present. This list shall NOT contain more than one + * instance of a specific BatFaultEnum value. When the Node detects all conditions contributing to a fault have been + * cleared, the corresponding BatFaultEnum value shall be removed from this list. An empty list shall indicate there + * are currently no active faults. The order of this list SHOULD have no significance. Clients interested in + * monitoring changes in active faults may subscribe to this attribute, or they may subscribe to BatFaultChange. + */ + public List activeBatFaults; // 18 list R V + /** + * This attribute shall provide a user-facing description of this battery, which SHOULD contain information required + * to identify a replacement, such as form factor, chemistry or preferred manufacturer. + */ + public String batReplacementDescription; // 19 string R V + /** + * Indicates the ID of the common or colloquial designation of the battery, as specified in + * BatCommonDesignationEnum. + */ + public BatCommonDesignationEnum batCommonDesignation; // 20 BatCommonDesignationEnum R V + /** + * Indicates the string representing the ANSI designation for the battery as specified in ANSI C18. + */ + public String batAnsiDesignation; // 21 string R V + /** + * Indicates the string representing the IEC designation for the battery as specified in IEC 60086. + */ + public String batIecDesignation; // 22 string R V + /** + * Indicates the ID of the preferred chemistry of the battery source as specified in BatApprovedChemistryEnum. + */ + public BatApprovedChemistryEnum batApprovedChemistry; // 23 BatApprovedChemistryEnum R V + /** + * Indicates the preferred minimum charge capacity rating in mAh of individual, user- or factory-serviceable battery + * cells or packs in the battery source. + */ + public Integer batCapacity; // 24 uint32 R V + /** + * Indicates the quantity of individual, user- or factory-serviceable battery cells or packs in the battery source. + */ + public Integer batQuantity; // 25 uint8 R V + /** + * Indicates the current state of the battery source with respect to charging as specified in BatChargeStateEnum. + */ + public BatChargeStateEnum batChargeState; // 26 BatChargeStateEnum R V + /** + * Indicates the estimated time in seconds before the battery source will be at full charge. A value of NULL shall + * indicate the Node is currently unable to assess the value. + * Changes to this attribute shall only be marked as reportable in the following cases: + * • At most once every 10 seconds, or + * • When it changes from null to any other value and vice versa. + * Since reporting consumes power, devices SHOULD be careful not to over-report. + */ + public Integer batTimeToFullCharge; // 27 uint32 R V + /** + * Indicates whether the Node can remain operational while the battery source is charging. + */ + public Boolean batFunctionalWhileCharging; // 28 bool R V + /** + * Indicates assessed current in mA (milliamps) presently supplied to charge the battery source. A value of NULL + * shall indicate the Node is currently unable to assess the value. + */ + public Integer batChargingCurrent; // 29 uint32 R V + /** + * Indicates the set of charge faults currently detected by the Node on this power source. This set is represented + * as a list of BatChargeFaultEnum. When the Node detects a fault has been raised, the appropriate + * BatChargeFaultEnum value shall be added to this list, provided it is not already present. This list shall NOT + * contain more than one instance of a specific BatChargeFaultEnum value. When the Node detects all conditions + * contributing to a fault have been cleared, the corresponding BatChargeFaultEnum value shall be removed from this + * list. An empty list shall indicate there are currently no active faults. The order of this list SHOULD have no + * significance. Clients interested in monitoring changes in active faults may subscribe to this attribute, or they + * may subscribe to the BatFaultChange event. + */ + public List activeBatChargeFaults; // 30 list R V + /** + * Indicates a list of endpoints that are powered by the source defined by this cluster. Multiple instances of this + * cluster may list the same endpoint, because it is possible for power for an endpoint to come from multiple + * sources. In that case the Order attribute indicates their priority. + * For each power source on a node, there shall only be one instance of this cluster. + * A cluster instance with an empty list shall indicate that the power source is for the entire node, which includes + * all endpoints. + * A cluster instance with a non-empty list shall include the endpoint, upon which the cluster instance resides. + * The above rules allow that some endpoints can have an unknown power source, and therefore would not be indicated + * by any instance of this cluster. + * Typically, there is one power source for the node. Also common is mains power for the node with battery backup + * power for the node. In both these common cases, for each cluster instance described, the list is empty. + * A node has a mains power source with Order as 0 (zero), but some application endpoints (not all) have a battery + * back up source with Order as 1, which means this list is empty for the Power Source cluster associated with the + * mains power, because it indicates the entire node, but the Power Source cluster instance associated with the + * battery backup would list the endpoints that have a battery backup. + */ + public List endpointList; // 31 list R V + // Structs + + /** + * The WiredFaultChange Event shall be generated when the set of wired faults currently detected by the Node on this + * wired power source changes. This event shall correspond to a change in value of ActiveWiredFaults. + */ + public class WiredFaultChange { + /** + * This field shall represent the set of faults currently detected, as per ActiveWiredFaults. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per ActiveWiredFaults. + */ + public List previous; // list + + public WiredFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + /** + * The BatFaultChange Event shall be generated when the set of battery faults currently detected by the Node on this + * battery power source changes. This event shall correspond to a change in value of ActiveBatFaults. + */ + public class BatFaultChange { + /** + * This field shall represent the set of faults currently detected, as per ActiveBatFaults. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per ActiveBatFaults. + */ + public List previous; // list + + public BatFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + /** + * The BatChargeFaultChange Event shall be generated when the set of charge faults currently detected by the Node on + * this battery power source changes. This event shall correspond to a change in value of ActiveBatChargeFaults. + */ + public class BatChargeFaultChange { + /** + * This field shall represent the set of faults currently detected, as per ActiveBatChargeFaults. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per + * ActiveBatChargeFaults. + */ + public List previous; // list + + public BatChargeFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + // Enums + public enum WiredFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + OVER_VOLTAGE(1, "Over Voltage"), + UNDER_VOLTAGE(2, "Under Voltage"); + + public final Integer value; + public final String label; + + private WiredFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + OVER_TEMP(1, "Over Temp"), + UNDER_TEMP(2, "Under Temp"); + + public final Integer value; + public final String label; + + private BatFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatChargeFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + AMBIENT_TOO_HOT(1, "Ambient Too Hot"), + AMBIENT_TOO_COLD(2, "Ambient Too Cold"), + BATTERY_TOO_HOT(3, "Battery Too Hot"), + BATTERY_TOO_COLD(4, "Battery Too Cold"), + BATTERY_ABSENT(5, "Battery Absent"), + BATTERY_OVER_VOLTAGE(6, "Battery Over Voltage"), + BATTERY_UNDER_VOLTAGE(7, "Battery Under Voltage"), + CHARGER_OVER_VOLTAGE(8, "Charger Over Voltage"), + CHARGER_UNDER_VOLTAGE(9, "Charger Under Voltage"), + SAFETY_TIMEOUT(10, "Safety Timeout"); + + public final Integer value; + public final String label; + + private BatChargeFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum PowerSourceStatusEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + ACTIVE(1, "Active"), + STANDBY(2, "Standby"), + UNAVAILABLE(3, "Unavailable"); + + public final Integer value; + public final String label; + + private PowerSourceStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum WiredCurrentTypeEnum implements MatterEnum { + AC(0, "Ac"), + DC(1, "Dc"); + + public final Integer value; + public final String label; + + private WiredCurrentTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatChargeLevelEnum implements MatterEnum { + OK(0, "Ok"), + WARNING(1, "Warning"), + CRITICAL(2, "Critical"); + + public final Integer value; + public final String label; + + private BatChargeLevelEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatReplaceabilityEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + NOT_REPLACEABLE(1, "Not Replaceable"), + USER_REPLACEABLE(2, "User Replaceable"), + FACTORY_REPLACEABLE(3, "Factory Replaceable"); + + public final Integer value; + public final String label; + + private BatReplaceabilityEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatCommonDesignationEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + AAA(1, "Aaa"), + AA(2, "Aa"), + C(3, "C"), + D(4, "D"), + V4V5(5, "4 V 5"), + V6V0(6, "6 V 0"), + V9V0(7, "9 V 0"), + V12AA(8, "12 Aa"), + AAAA(9, "Aaaa"), + A(10, "A"), + B(11, "B"), + F(12, "F"), + N(13, "N"), + NO6(14, "No 6"), + SUB_C(15, "Sub C"), + A23(16, "A 23"), + A27(17, "A 27"), + BA5800(18, "Ba 5800"), + DUPLEX(19, "Duplex"), + V4SR44(20, "4 Sr 44"), + V523(21, "523"), + V531(22, "531"), + V15V0(23, "15 V 0"), + V22V5(24, "22 V 5"), + V30V0(25, "30 V 0"), + V45V0(26, "45 V 0"), + V67V5(27, "67 V 5"), + J(28, "J"), + CR123A(29, "Cr 123 A"), + CR2(30, "Cr 2"), + V2CR5(31, "2 Cr 5"), + CR_P2(32, "Cr P 2"), + CR_V3(33, "Cr V 3"), + SR41(34, "Sr 41"), + SR43(35, "Sr 43"), + SR44(36, "Sr 44"), + SR45(37, "Sr 45"), + SR48(38, "Sr 48"), + SR54(39, "Sr 54"), + SR55(40, "Sr 55"), + SR57(41, "Sr 57"), + SR58(42, "Sr 58"), + SR59(43, "Sr 59"), + SR60(44, "Sr 60"), + SR63(45, "Sr 63"), + SR64(46, "Sr 64"), + SR65(47, "Sr 65"), + SR66(48, "Sr 66"), + SR67(49, "Sr 67"), + SR68(50, "Sr 68"), + SR69(51, "Sr 69"), + SR516(52, "Sr 516"), + SR731(53, "Sr 731"), + SR712(54, "Sr 712"), + LR932(55, "Lr 932"), + A5(56, "A 5"), + A10(57, "A 10"), + A13(58, "A 13"), + A312(59, "A 312"), + A675(60, "A 675"), + AC41E(61, "Ac 41 E"), + V10180(62, "10180"), + V10280(63, "10280"), + V10440(64, "10440"), + V14250(65, "14250"), + V14430(66, "14430"), + V14500(67, "14500"), + V14650(68, "14650"), + V15270(69, "15270"), + V16340(70, "16340"), + RCR123A(71, "Rcr 123 A"), + V17500(72, "17500"), + V17670(73, "17670"), + V18350(74, "18350"), + V18500(75, "18500"), + V18650(76, "18650"), + V19670(77, "19670"), + V25500(78, "25500"), + V26650(79, "26650"), + V32600(80, "32600"); + + public final Integer value; + public final String label; + + private BatCommonDesignationEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatApprovedChemistryEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + ALKALINE(1, "Alkaline"), + LITHIUM_CARBON_FLUORIDE(2, "Lithium Carbon Fluoride"), + LITHIUM_CHROMIUM_OXIDE(3, "Lithium Chromium Oxide"), + LITHIUM_COPPER_OXIDE(4, "Lithium Copper Oxide"), + LITHIUM_IRON_DISULFIDE(5, "Lithium Iron Disulfide"), + LITHIUM_MANGANESE_DIOXIDE(6, "Lithium Manganese Dioxide"), + LITHIUM_THIONYL_CHLORIDE(7, "Lithium Thionyl Chloride"), + MAGNESIUM(8, "Magnesium"), + MERCURY_OXIDE(9, "Mercury Oxide"), + NICKEL_OXYHYDRIDE(10, "Nickel Oxyhydride"), + SILVER_OXIDE(11, "Silver Oxide"), + ZINC_AIR(12, "Zinc Air"), + ZINC_CARBON(13, "Zinc Carbon"), + ZINC_CHLORIDE(14, "Zinc Chloride"), + ZINC_MANGANESE_DIOXIDE(15, "Zinc Manganese Dioxide"), + LEAD_ACID(16, "Lead Acid"), + LITHIUM_COBALT_OXIDE(17, "Lithium Cobalt Oxide"), + LITHIUM_ION(18, "Lithium Ion"), + LITHIUM_ION_POLYMER(19, "Lithium Ion Polymer"), + LITHIUM_IRON_PHOSPHATE(20, "Lithium Iron Phosphate"), + LITHIUM_SULFUR(21, "Lithium Sulfur"), + LITHIUM_TITANATE(22, "Lithium Titanate"), + NICKEL_CADMIUM(23, "Nickel Cadmium"), + NICKEL_HYDROGEN(24, "Nickel Hydrogen"), + NICKEL_IRON(25, "Nickel Iron"), + NICKEL_METAL_HYDRIDE(26, "Nickel Metal Hydride"), + NICKEL_ZINC(27, "Nickel Zinc"), + SILVER_ZINC(28, "Silver Zinc"), + SODIUM_ION(29, "Sodium Ion"), + SODIUM_SULFUR(30, "Sodium Sulfur"), + ZINC_BROMIDE(31, "Zinc Bromide"), + ZINC_CERIUM(32, "Zinc Cerium"); + + public final Integer value; + public final String label; + + private BatApprovedChemistryEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum BatChargeStateEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + IS_CHARGING(1, "Is Charging"), + IS_AT_FULL_CHARGE(2, "Is At Full Charge"), + IS_NOT_CHARGING(3, "Is Not Charging"); + + public final Integer value; + public final String label; + + private BatChargeStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * A wired power source + */ + public boolean wired; + /** + * + * A battery power source + */ + public boolean battery; + /** + * + * A rechargeable battery power source + */ + public boolean rechargeable; + /** + * + * A replaceable battery power source + */ + public boolean replaceable; + + public FeatureMap(boolean wired, boolean battery, boolean rechargeable, boolean replaceable) { + this.wired = wired; + this.battery = battery; + this.rechargeable = rechargeable; + this.replaceable = replaceable; + } + } + + public PowerSourceCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 47, "PowerSource"); + } + + protected PowerSourceCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "status : " + status + "\n"; + str += "order : " + order + "\n"; + str += "description : " + description + "\n"; + str += "wiredAssessedInputVoltage : " + wiredAssessedInputVoltage + "\n"; + str += "wiredAssessedInputFrequency : " + wiredAssessedInputFrequency + "\n"; + str += "wiredCurrentType : " + wiredCurrentType + "\n"; + str += "wiredAssessedCurrent : " + wiredAssessedCurrent + "\n"; + str += "wiredNominalVoltage : " + wiredNominalVoltage + "\n"; + str += "wiredMaximumCurrent : " + wiredMaximumCurrent + "\n"; + str += "wiredPresent : " + wiredPresent + "\n"; + str += "activeWiredFaults : " + activeWiredFaults + "\n"; + str += "batVoltage : " + batVoltage + "\n"; + str += "batPercentRemaining : " + batPercentRemaining + "\n"; + str += "batTimeRemaining : " + batTimeRemaining + "\n"; + str += "batChargeLevel : " + batChargeLevel + "\n"; + str += "batReplacementNeeded : " + batReplacementNeeded + "\n"; + str += "batReplaceability : " + batReplaceability + "\n"; + str += "batPresent : " + batPresent + "\n"; + str += "activeBatFaults : " + activeBatFaults + "\n"; + str += "batReplacementDescription : " + batReplacementDescription + "\n"; + str += "batCommonDesignation : " + batCommonDesignation + "\n"; + str += "batAnsiDesignation : " + batAnsiDesignation + "\n"; + str += "batIecDesignation : " + batIecDesignation + "\n"; + str += "batApprovedChemistry : " + batApprovedChemistry + "\n"; + str += "batCapacity : " + batCapacity + "\n"; + str += "batQuantity : " + batQuantity + "\n"; + str += "batChargeState : " + batChargeState + "\n"; + str += "batTimeToFullCharge : " + batTimeToFullCharge + "\n"; + str += "batFunctionalWhileCharging : " + batFunctionalWhileCharging + "\n"; + str += "batChargingCurrent : " + batChargingCurrent + "\n"; + str += "activeBatChargeFaults : " + activeBatChargeFaults + "\n"; + str += "endpointList : " + endpointList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerSourceConfigurationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerSourceConfigurationCluster.java new file mode 100644 index 00000000000..5ebba5c8a55 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerSourceConfigurationCluster.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * PowerSourceConfiguration + * + * @author Dan Cunningham - Initial contribution + */ +public class PowerSourceConfigurationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x002E; + public static final String CLUSTER_NAME = "PowerSourceConfiguration"; + public static final String CLUSTER_PREFIX = "powerSourceConfiguration"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_SOURCES = "sources"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * This list shall contain the set of all power sources capable of participating in the power system of this Node. + * Each entry in the list shall be the endpoint number of an endpoint having a Power Source cluster, which + * corresponds to a physical power source. The endpoint number shall be unique within the list. + * The order of power sources on a Node is defined by the Order attribute of its associated Power Source cluster + * provided on the endpoint. List entries shall be sorted in increasing order, that is, an entry with a lower order + * shall have a lower index than any entry with a higher order. Multiple entries may have the same order, there are + * no restrictions on their relative sorting. + */ + public List sources; // 0 list R V + + public PowerSourceConfigurationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 46, "PowerSourceConfiguration"); + } + + protected PowerSourceConfigurationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "sources : " + sources + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerTopologyCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerTopologyCluster.java new file mode 100644 index 00000000000..e013ce1106a --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PowerTopologyCluster.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * PowerTopology + * + * @author Dan Cunningham - Initial contribution + */ +public class PowerTopologyCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x009C; + public static final String CLUSTER_NAME = "PowerTopology"; + public static final String CLUSTER_PREFIX = "powerTopology"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_AVAILABLE_ENDPOINTS = "availableEndpoints"; + public static final String ATTRIBUTE_ACTIVE_ENDPOINTS = "activeEndpoints"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the list of endpoints capable of providing power to and/or consuming power from the endpoint hosting + * this server. + */ + public List availableEndpoints; // 0 list R V + /** + * Indicates the current list of endpoints currently providing or consuming power to or from the endpoint hosting + * this server. This list shall be a subset of the value of the AvailableEndpoints attribute. + */ + public List activeEndpoints; // 1 list R V + + // Bitmaps + public static class FeatureMap { + /** + * + * This endpoint provides or consumes power to/from the entire node + */ + public boolean nodeTopology; + /** + * + * This endpoint provides or consumes power to/from itself and its child endpoints + */ + public boolean treeTopology; + /** + * + * This endpoint provides or consumes power to/from a specified set of endpoints + */ + public boolean setTopology; + /** + * + * The specified set of endpoints may change + */ + public boolean dynamicPowerFlow; + + public FeatureMap(boolean nodeTopology, boolean treeTopology, boolean setTopology, boolean dynamicPowerFlow) { + this.nodeTopology = nodeTopology; + this.treeTopology = treeTopology; + this.setTopology = setTopology; + this.dynamicPowerFlow = dynamicPowerFlow; + } + } + + public PowerTopologyCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 156, "PowerTopology"); + } + + protected PowerTopologyCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "availableEndpoints : " + availableEndpoints + "\n"; + str += "activeEndpoints : " + activeEndpoints + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PressureMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PressureMeasurementCluster.java new file mode 100644 index 00000000000..20ec16fde3a --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PressureMeasurementCluster.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * PressureMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class PressureMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0403; + public static final String CLUSTER_NAME = "PressureMeasurement"; + public static final String CLUSTER_PREFIX = "pressureMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_MEASURED_VALUE = "measuredValue"; + public static final String ATTRIBUTE_MIN_MEASURED_VALUE = "minMeasuredValue"; + public static final String ATTRIBUTE_MAX_MEASURED_VALUE = "maxMeasuredValue"; + public static final String ATTRIBUTE_TOLERANCE = "tolerance"; + public static final String ATTRIBUTE_SCALED_VALUE = "scaledValue"; + public static final String ATTRIBUTE_MIN_SCALED_VALUE = "minScaledValue"; + public static final String ATTRIBUTE_MAX_SCALED_VALUE = "maxScaledValue"; + public static final String ATTRIBUTE_SCALED_TOLERANCE = "scaledTolerance"; + public static final String ATTRIBUTE_SCALE = "scale"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the pressure in kPa as follows: + * MeasuredValue = 10 x Pressure [kPa] + * The null value indicates that the value is not available. + */ + public Integer measuredValue; // 0 int16 R V + /** + * Indicates the minimum value of MeasuredValue that can be measured. See Measured Value for more details. + * The null value indicates that the value is not available. + */ + public Integer minMeasuredValue; // 1 int16 R V + /** + * Indicates the maximum value of MeasuredValue that can be measured. See Measured Value for more details. + * The null value indicates that the value is not available. + */ + public Integer maxMeasuredValue; // 2 int16 R V + /** + * See Measured Value. + */ + public Integer tolerance; // 3 uint16 R V + /** + * Indicates the pressure in Pascals as follows: ScaledValue = 10Scale x Pressure [Pa] + * The null value indicates that the value is not available. + */ + public Integer scaledValue; // 16 int16 R V + /** + * Indicates the minimum value of ScaledValue that can be measured. The null value indicates that the value is not + * available. + */ + public Integer minScaledValue; // 17 int16 R V + /** + * Indicates the maximum value of ScaledValue that can be measured. The null value indicates that the value is not + * available. + */ + public Integer maxScaledValue; // 18 int16 R V + /** + * Indicates the magnitude of the possible error that is associated with ScaledValue. The true value is located in + * the range + * (ScaledValue – ScaledTolerance) to (ScaledValue + ScaledTolerance). + */ + public Integer scaledTolerance; // 19 uint16 R V + /** + * Indicates the base 10 exponent used to obtain ScaledValue (see ScaledValue). + */ + public Integer scale; // 20 int8 R V + + // Bitmaps + public static class FeatureMap { + /** + * + * Extended range and resolution + */ + public boolean extended; + + public FeatureMap(boolean extended) { + this.extended = extended; + } + } + + public PressureMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1027, "PressureMeasurement"); + } + + protected PressureMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "measuredValue : " + measuredValue + "\n"; + str += "minMeasuredValue : " + minMeasuredValue + "\n"; + str += "maxMeasuredValue : " + maxMeasuredValue + "\n"; + str += "tolerance : " + tolerance + "\n"; + str += "scaledValue : " + scaledValue + "\n"; + str += "minScaledValue : " + minScaledValue + "\n"; + str += "maxScaledValue : " + maxScaledValue + "\n"; + str += "scaledTolerance : " + scaledTolerance + "\n"; + str += "scale : " + scale + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ProxyConfigurationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ProxyConfigurationCluster.java new file mode 100644 index 00000000000..1bddfaa340d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ProxyConfigurationCluster.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ProxyConfiguration + * + * @author Dan Cunningham - Initial contribution + */ +public class ProxyConfigurationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0042; + public static final String CLUSTER_NAME = "ProxyConfiguration"; + public static final String CLUSTER_PREFIX = "proxyConfiguration"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_CONFIGURATION_LIST = "configurationList"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * List of proxy configurations. There shall NOT be multiple entries in this list for the same fabric. + */ + public List configurationList; // 0 list RW + // Structs + + public class ConfigurationStruct { + /** + * This field shall be set to true to indicate to the proxy that it shall proxy all nodes. When true, the + * SourceList attribute is ignored. + */ + public Boolean proxyAllNodes; // bool + /** + * When ProxyAllNodes is false, this list contains the set of Node IDs of sources that this proxy shall + * specifically proxy. + */ + public List sourceList; // list + + public ConfigurationStruct(Boolean proxyAllNodes, List sourceList) { + this.proxyAllNodes = proxyAllNodes; + this.sourceList = sourceList; + } + } + + public ProxyConfigurationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 66, "ProxyConfiguration"); + } + + protected ProxyConfigurationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "configurationList : " + configurationList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ProxyDiscoveryCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ProxyDiscoveryCluster.java new file mode 100644 index 00000000000..06249d08c16 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ProxyDiscoveryCluster.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ProxyDiscovery + * + * @author Dan Cunningham - Initial contribution + */ +public class ProxyDiscoveryCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0043; + public static final String CLUSTER_NAME = "ProxyDiscovery"; + public static final String CLUSTER_PREFIX = "proxyDiscovery"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + + public Integer clusterRevision; // 65533 ClusterRevision + + public ProxyDiscoveryCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 67, "ProxyDiscovery"); + } + + protected ProxyDiscoveryCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used during proxy discovery, as specified in Section 9.15.7, “Proxy Discovery & Assignment + * Flow”. + */ + public static ClusterCommand proxyDiscoverRequest(BigInteger sourceNodeId, Integer numAttributePaths, + Integer numEventPaths) { + Map map = new LinkedHashMap<>(); + if (sourceNodeId != null) { + map.put("sourceNodeId", sourceNodeId); + } + if (numAttributePaths != null) { + map.put("numAttributePaths", numAttributePaths); + } + if (numEventPaths != null) { + map.put("numEventPaths", numEventPaths); + } + return new ClusterCommand("proxyDiscoverRequest", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PumpConfigurationAndControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PumpConfigurationAndControlCluster.java new file mode 100644 index 00000000000..17802215a9c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/PumpConfigurationAndControlCluster.java @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * PumpConfigurationAndControl + * + * @author Dan Cunningham - Initial contribution + */ +public class PumpConfigurationAndControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0200; + public static final String CLUSTER_NAME = "PumpConfigurationAndControl"; + public static final String CLUSTER_PREFIX = "pumpConfigurationAndControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_MAX_PRESSURE = "maxPressure"; + public static final String ATTRIBUTE_MAX_SPEED = "maxSpeed"; + public static final String ATTRIBUTE_MAX_FLOW = "maxFlow"; + public static final String ATTRIBUTE_MIN_CONST_PRESSURE = "minConstPressure"; + public static final String ATTRIBUTE_MAX_CONST_PRESSURE = "maxConstPressure"; + public static final String ATTRIBUTE_MIN_COMP_PRESSURE = "minCompPressure"; + public static final String ATTRIBUTE_MAX_COMP_PRESSURE = "maxCompPressure"; + public static final String ATTRIBUTE_MIN_CONST_SPEED = "minConstSpeed"; + public static final String ATTRIBUTE_MAX_CONST_SPEED = "maxConstSpeed"; + public static final String ATTRIBUTE_MIN_CONST_FLOW = "minConstFlow"; + public static final String ATTRIBUTE_MAX_CONST_FLOW = "maxConstFlow"; + public static final String ATTRIBUTE_MIN_CONST_TEMP = "minConstTemp"; + public static final String ATTRIBUTE_MAX_CONST_TEMP = "maxConstTemp"; + public static final String ATTRIBUTE_PUMP_STATUS = "pumpStatus"; + public static final String ATTRIBUTE_EFFECTIVE_OPERATION_MODE = "effectiveOperationMode"; + public static final String ATTRIBUTE_EFFECTIVE_CONTROL_MODE = "effectiveControlMode"; + public static final String ATTRIBUTE_CAPACITY = "capacity"; + public static final String ATTRIBUTE_SPEED = "speed"; + public static final String ATTRIBUTE_LIFETIME_RUNNING_HOURS = "lifetimeRunningHours"; + public static final String ATTRIBUTE_POWER = "power"; + public static final String ATTRIBUTE_LIFETIME_ENERGY_CONSUMED = "lifetimeEnergyConsumed"; + public static final String ATTRIBUTE_OPERATION_MODE = "operationMode"; + public static final String ATTRIBUTE_CONTROL_MODE = "controlMode"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute specifies the maximum pressure the pump can achieve. It is a physical limit, and does not apply to + * any specific control mode or operation mode. + * Valid range is -3,276.7 kPa to 3,276.7 kPa (steps of 0.1 kPa). Null if the value is invalid. + */ + public Integer maxPressure; // 0 int16 R V + /** + * This attribute specifies the maximum speed the pump can achieve. It is a physical limit, and does not apply to + * any specific control mode or operation mode. + * Valid range is 0 to 65,534 RPM (steps of 1 RPM). Null if the value is invalid. + */ + public Integer maxSpeed; // 1 uint16 R V + /** + * This attribute specifies the maximum flow the pump can achieve. It is a physical limit, and does not apply to any + * specific control mode or operation mode. + * Valid range is 0 m/h to 6,553.4 m/h (steps of 0.1 m/h). Null if the value is invalid. + */ + public Integer maxFlow; // 2 uint16 R V + /** + * This attribute specifies the minimum pressure the pump can achieve when it is working with the ControlMode + * attribute set to ConstantPressure. + * Valid range is –3,276.7 kPa to 3,276.7 kPa (steps of 0.1 kPa). Null if the value is invalid. + */ + public Integer minConstPressure; // 3 int16 R V + /** + * This attribute specifies the maximum pressure the pump can achieve when it is working with the ControlMode + * attribute set to ConstantPressure. + * Valid range is –3,276.7 kPa to 3,276.7 kPa (steps of 0.1 kPa). Null if the value is invalid. + */ + public Integer maxConstPressure; // 4 int16 R V + /** + * This attribute specifies the minimum compensated pressure the pump can achieve when it is working with the + * ControlMode attribute set to ProportionalPressure. + * Valid range is –3,276.7 kPa to 3,276.7 kPa (steps of 0.1 kPa). Null if the value is invalid. + */ + public Integer minCompPressure; // 5 int16 R V + /** + * This attribute specifies the maximum compensated pressure the pump can achieve when it is working with the + * ControlMode attribute set to ProportionalPressure. + * Valid range is –3,276.7 kPa to 3,276.7 kPa (steps of 0.1 kPa). Null if the value is invalid. + */ + public Integer maxCompPressure; // 6 int16 R V + /** + * This attribute specifies the minimum speed the pump can achieve when it is working with the ControlMode attribute + * set to ConstantSpeed. + * Valid range is 0 to 65,534 RPM (steps of 1 RPM). Null if the value is invalid. + */ + public Integer minConstSpeed; // 7 uint16 R V + /** + * This attribute specifies the maximum speed the pump can achieve when it is working with the ControlMode attribute + * set to ConstantSpeed. + * Valid range is 0 to 65,534 RPM (steps of 1 RPM). Null if the value is invalid. + */ + public Integer maxConstSpeed; // 8 uint16 R V + /** + * This attribute specifies the minimum flow the pump can achieve when it is working with the ControlMode attribute + * set to ConstantFlow. + * Valid range is 0 m/h to 6,553.4 m/h (steps of 0.1 m/h). Null if the value is invalid. + */ + public Integer minConstFlow; // 9 uint16 R V + /** + * This attribute specifies the maximum flow the pump can achieve when it is working with the ControlMode attribute + * set to ConstantFlow. + * Valid range is 0 m/h to 6,553.4 m/h (steps of 0.1 m/h). Null if the value is invalid. + */ + public Integer maxConstFlow; // 10 uint16 R V + /** + * This attribute specifies the minimum temperature the pump can maintain in the system when it is working with the + * ControlMode attribute set to ConstantTemperature. + * Valid range is –273.15 °C to 327.67 °C (steps of 0.01 °C). Null if the value is invalid. + */ + public Integer minConstTemp; // 11 int16 R V + /** + * This attribute specifies the maximum temperature the pump can maintain in the system when it is working with the + * ControlMode attribute set to ConstantTemperature. + * MaxConstTemp shall be greater than or equal to MinConstTemp Valid range is –273.15 °C to 327.67 °C (steps of 0.01 + * °C). Null if the value is invalid. + */ + public Integer maxConstTemp; // 12 int16 R V + /** + * This attribute specifies the activity status of the pump functions as listed in PumpStatusBitmap. Where a pump + * controller function is active, the corresponding bit shall be set to 1. Where a pump controller function is not + * active, the corresponding bit shall be set to 0. + */ + public PumpStatusBitmap pumpStatus; // 16 PumpStatusBitmap R V + /** + * This attribute specifies current effective operation mode of the pump as defined in OperationModeEnum. + * The value of the EffectiveOperationMode attribute is the same as the OperationMode attribute, unless one of the + * following points are true: + * • The pump is physically set to run with the local settings + * • The LocalOverride bit in the PumpStatus attribute is set, + * See OperationMode and ControlMode attributes for a detailed description of the operation and control of the pump. + */ + public OperationModeEnum effectiveOperationMode; // 17 OperationModeEnum R V + /** + * This attribute specifies the current effective control mode of the pump as defined in ControlModeEnum. + * This attribute contains the control mode that currently applies to the pump. It will have the value of the + * ControlMode attribute, unless one of the following points are true: + * • The ControlMode attribute is set to Automatic. In this case, the value of the EffectiveControlMode shall match + * the behavior of the pump. + * • A remote sensor is used as the sensor for regulation of the pump. In this case, EffectiveControlMode will + * display ConstantPressure, ConstantFlow or ConstantTemperature if the remote sensor is a pressure sensor, a flow + * sensor or a temperature sensor respectively, regardless of the value of the ControlMode attribute. + * In case the ControlMode attribute is not included on the device and no remote sensors are connected, the value of + * the EffectiveControlMode shall match the vendor-specific behavior of the pump. + * See OperationMode and ControlMode attributes for detailed a description of the operation and control of the pump. + */ + public ControlModeEnum effectiveControlMode; // 18 ControlModeEnum R V + /** + * This attribute specifies the actual capacity of the pump as a percentage of the effective maximum setpoint value. + * It is updated dynamically as the speed of the pump changes. + * If the value is not available (the measurement or estimation of the speed is done in the pump), this attribute + * will indicate the null value. + * Valid range is 0 % to 163.835% (0.005 % granularity). Although this attribute is a signed value, values of + * capacity less than zero have no physical meaning. + */ + public Integer capacity; // 19 int16 R V + /** + * This attribute specifies the actual speed of the pump measured in RPM. It is updated dynamically as the speed of + * the pump changes. + * If the value is not available (the measurement or estimation of the speed is done in the pump), this attribute + * will indicate the null value. + * Valid range is 0 to 65,534 RPM. + */ + public Integer speed; // 20 uint16 R V + /** + * This attribute specifies the accumulated number of hours that the pump has been powered and the motor has been + * running. It is updated dynamically as it increases. It is preserved over power cycles of the pump. If + * LifeTimeRunningHours rises above maximum value it “rolls over” and starts at 0 (zero). + * This attribute is writeable, in order to allow setting to an appropriate value after maintenance. If the value is + * not available, this attribute will indicate the null value. + * Valid range is 0 to 16,777,214 hrs. + */ + public Integer lifetimeRunningHours; // 21 uint24 RW VM + /** + * This attribute specifies the actual power consumption of the pump in Watts. The value of this attribute is + * updated dynamically as the power consumption of the pump changes. + * This attribute is read only. If the value is not available (the measurement of power consumption is not done in + * the pump), this attribute will indicate the null value. + * Valid range is 0 to 16,777,214 Watts. + */ + public Integer power; // 22 uint24 R V + /** + * This attribute specifies the accumulated energy consumption of the pump through the entire lifetime of the pump + * in kWh. The value of the LifetimeEnergyConsumed attribute is updated dynamically as the energy consumption of the + * pump increases. If LifetimeEnergyConsumed rises above maximum value it “rolls over” and starts at 0 (zero). + * This attribute is writeable, in order to allow setting to an appropriate value after maintenance. + * Valid range is 0 kWh to 4,294,967,294 kWh. + * Null if the value is unknown. + */ + public Integer lifetimeEnergyConsumed; // 23 uint32 RW VM + /** + * This attribute specifies the operation mode of the pump as defined in OperationModeEnum. + * The actual operating mode of the pump is a result of the setting of the attributes OperationMode, ControlMode and + * the optional connection of a remote sensor. The operation and control is prioritized as shown in the scheme + * below: + * ### Priority Scheme of Pump Operation and Control + * If this attribute is Maximum, Minimum or Local, the OperationMode attribute decides how the pump is operated. + * If this attribute is Normal and a remote sensor is connected to the pump, the type of the remote sensor decides + * the control mode of the pump. A connected remote pressure sensor will make the pump run in control mode Constant + * pressure and vice versa for flow and temperature type sensors. This is regardless of the setting of the + * ControlMode attribute. + * If this attribute is Normal and no remote sensor is connected, the control mode of the pump is decided by the + * ControlMode attribute. + * OperationMode may be changed at any time, even when the pump is running. The behavior of the pump at the point of + * changing the value of this attribute is vendor-specific. + * In the case a device does not support a specific operation mode, the write interaction to this attribute with an + * unsupported operation mode value shall be ignored and a response containing the status of CONSTRAINT_ERROR shall + * be returned. + */ + public OperationModeEnum operationMode; // 32 OperationModeEnum RW VM + /** + * This attribute specifies the control mode of the pump as defined in ControlModeEnum. + * See the OperationMode attribute for a detailed description of the operation and control of the pump. + * ControlMode may be changed at any time, even when the pump is running. The behavior of the pump at the point of + * changing is vendor-specific. + * In the case a device does not support a specific control mode, the write interaction to this attribute with an + * unsupported control mode value shall be ignored and a response containing the status of CONSTRAINT_ERROR shall be + * returned. + */ + public ControlModeEnum controlMode; // 33 ControlModeEnum RW VM + // Structs + + public class SupplyVoltageLow { + public SupplyVoltageLow() { + } + } + + public class SupplyVoltageHigh { + public SupplyVoltageHigh() { + } + } + + public class PowerMissingPhase { + public PowerMissingPhase() { + } + } + + public class SystemPressureLow { + public SystemPressureLow() { + } + } + + public class SystemPressureHigh { + public SystemPressureHigh() { + } + } + + public class DryRunning { + public DryRunning() { + } + } + + public class MotorTemperatureHigh { + public MotorTemperatureHigh() { + } + } + + public class PumpMotorFatalFailure { + public PumpMotorFatalFailure() { + } + } + + public class ElectronicTemperatureHigh { + public ElectronicTemperatureHigh() { + } + } + + public class PumpBlocked { + public PumpBlocked() { + } + } + + public class SensorFailure { + public SensorFailure() { + } + } + + public class ElectronicNonFatalFailure { + public ElectronicNonFatalFailure() { + } + } + + public class ElectronicFatalFailure { + public ElectronicFatalFailure() { + } + } + + public class GeneralFault { + public GeneralFault() { + } + } + + public class Leakage { + public Leakage() { + } + } + + public class AirDetection { + public AirDetection() { + } + } + + public class TurbineOperation { + public TurbineOperation() { + } + } + + // Enums + public enum OperationModeEnum implements MatterEnum { + NORMAL(0, "Normal"), + MINIMUM(1, "Minimum"), + MAXIMUM(2, "Maximum"), + LOCAL(3, "Local"); + + public final Integer value; + public final String label; + + private OperationModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ControlModeEnum implements MatterEnum { + CONSTANT_SPEED(0, "Constant Speed"), + CONSTANT_PRESSURE(1, "Constant Pressure"), + PROPORTIONAL_PRESSURE(2, "Proportional Pressure"), + CONSTANT_FLOW(3, "Constant Flow"), + CONSTANT_TEMPERATURE(5, "Constant Temperature"), + AUTOMATIC(7, "Automatic"); + + public final Integer value; + public final String label; + + private ControlModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class PumpStatusBitmap { + /** + * A fault related to the system or pump device is detected. + * If this bit is set, it may correspond to an event in the range 2-16, see Events. + */ + public boolean deviceFault; + /** + * A fault related to the supply to the pump is detected. + * If this bit is set, it may correspond to an event in the range 0-1 or 13, see Events. + */ + public boolean supplyFault; + public boolean speedLow; + public boolean speedHigh; + /** + * Device control is overridden by hardware, such as an external STOP button or via a local HMI. + * While this bit is set, the EffectiveOperationMode is adjusted to Local. Any request changing OperationMode + * shall generate a FAILURE error status until LocalOverride is cleared on the physical device. When + * LocalOverride is cleared, the device shall return to the operation mode set in OperationMode. + */ + public boolean localOverride; + public boolean running; + /** + * A remote pressure sensor is used as the sensor for the regulation of the pump. + * If this bit is set, EffectiveControlMode is ConstantPressure and the setpoint for the pump is interpreted as + * a percentage of the range of the remote sensor ([MinMeasuredValue – MaxMeasuredValue]). + */ + public boolean remotePressure; + /** + * A remote flow sensor is used as the sensor for the regulation of the pump. + * If this bit is set, EffectiveControlMode is ConstantFlow, and the setpoint for the pump is interpreted as a + * percentage of the range of the remote sensor ([MinMeasuredValue – MaxMeasuredValue]). + */ + public boolean remoteFlow; + /** + * A remote temperature sensor is used as the sensor for the regulation of the pump. + * If this bit is set, EffectiveControlMode is ConstantTemperature, and the setpoint for the pump is interpreted + * as a percentage of the range of the remote sensor ([MinMeasuredValue – MaxMeasuredValue]) + */ + public boolean remoteTemperature; + + public PumpStatusBitmap(boolean deviceFault, boolean supplyFault, boolean speedLow, boolean speedHigh, + boolean localOverride, boolean running, boolean remotePressure, boolean remoteFlow, + boolean remoteTemperature) { + this.deviceFault = deviceFault; + this.supplyFault = supplyFault; + this.speedLow = speedLow; + this.speedHigh = speedHigh; + this.localOverride = localOverride; + this.running = running; + this.remotePressure = remotePressure; + this.remoteFlow = remoteFlow; + this.remoteTemperature = remoteTemperature; + } + } + + public static class FeatureMap { + /** + * + * Supports operating in constant pressure mode + */ + public boolean constantPressure; + /** + * + * Supports operating in compensated pressure mode + */ + public boolean compensatedPressure; + /** + * + * Supports operating in constant flow mode + */ + public boolean constantFlow; + /** + * + * Supports operating in constant speed mode + */ + public boolean constantSpeed; + /** + * + * Supports operating in constant temperature mode + */ + public boolean constantTemperature; + /** + * + * Supports operating in automatic mode + */ + public boolean automatic; + /** + * + * Supports operating using local settings + */ + public boolean localOperation; + + public FeatureMap(boolean constantPressure, boolean compensatedPressure, boolean constantFlow, + boolean constantSpeed, boolean constantTemperature, boolean automatic, boolean localOperation) { + this.constantPressure = constantPressure; + this.compensatedPressure = compensatedPressure; + this.constantFlow = constantFlow; + this.constantSpeed = constantSpeed; + this.constantTemperature = constantTemperature; + this.automatic = automatic; + this.localOperation = localOperation; + } + } + + public PumpConfigurationAndControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 512, "PumpConfigurationAndControl"); + } + + protected PumpConfigurationAndControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "maxPressure : " + maxPressure + "\n"; + str += "maxSpeed : " + maxSpeed + "\n"; + str += "maxFlow : " + maxFlow + "\n"; + str += "minConstPressure : " + minConstPressure + "\n"; + str += "maxConstPressure : " + maxConstPressure + "\n"; + str += "minCompPressure : " + minCompPressure + "\n"; + str += "maxCompPressure : " + maxCompPressure + "\n"; + str += "minConstSpeed : " + minConstSpeed + "\n"; + str += "maxConstSpeed : " + maxConstSpeed + "\n"; + str += "minConstFlow : " + minConstFlow + "\n"; + str += "maxConstFlow : " + maxConstFlow + "\n"; + str += "minConstTemp : " + minConstTemp + "\n"; + str += "maxConstTemp : " + maxConstTemp + "\n"; + str += "pumpStatus : " + pumpStatus + "\n"; + str += "effectiveOperationMode : " + effectiveOperationMode + "\n"; + str += "effectiveControlMode : " + effectiveControlMode + "\n"; + str += "capacity : " + capacity + "\n"; + str += "speed : " + speed + "\n"; + str += "lifetimeRunningHours : " + lifetimeRunningHours + "\n"; + str += "power : " + power + "\n"; + str += "lifetimeEnergyConsumed : " + lifetimeEnergyConsumed + "\n"; + str += "operationMode : " + operationMode + "\n"; + str += "controlMode : " + controlMode + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RadonConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RadonConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..f7dbcaef259 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RadonConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * RadonConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class RadonConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x042F; + public static final String CLUSTER_NAME = "RadonConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "radonConcentrationMeasurement"; + + public RadonConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1071, "RadonConcentrationMeasurement"); + } + + protected RadonConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RefrigeratorAlarmCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RefrigeratorAlarmCluster.java new file mode 100644 index 00000000000..320dfbb16e9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RefrigeratorAlarmCluster.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * RefrigeratorAlarm + * + * @author Dan Cunningham - Initial contribution + */ +public class RefrigeratorAlarmCluster extends AlarmBaseCluster { + + public static final int CLUSTER_ID = 0x0057; + public static final String CLUSTER_NAME = "RefrigeratorAlarm"; + public static final String CLUSTER_PREFIX = "refrigeratorAlarm"; + + // Bitmaps + public static class AlarmBitmap { + public boolean doorOpen; + + public AlarmBitmap(boolean doorOpen) { + this.doorOpen = doorOpen; + } + } + + public RefrigeratorAlarmCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 87, "RefrigeratorAlarm"); + } + + protected RefrigeratorAlarmCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RefrigeratorAndTemperatureControlledCabinetModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RefrigeratorAndTemperatureControlledCabinetModeCluster.java new file mode 100644 index 00000000000..f0135f12e3b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RefrigeratorAndTemperatureControlledCabinetModeCluster.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * RefrigeratorAndTemperatureControlledCabinetMode + * + * @author Dan Cunningham - Initial contribution + */ +public class RefrigeratorAndTemperatureControlledCabinetModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x0052; + public static final String CLUSTER_NAME = "RefrigeratorAndTemperatureControlledCabinetMode"; + public static final String CLUSTER_PREFIX = "refrigeratorAndTemperatureControlledCabinetMode"; + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + RAPID_COOL(16384, "Rapid Cool"), + RAPID_FREEZE(16385, "Rapid Freeze"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public RefrigeratorAndTemperatureControlledCabinetModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 82, "RefrigeratorAndTemperatureControlledCabinetMode"); + } + + protected RefrigeratorAndTemperatureControlledCabinetModeCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RelativeHumidityMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RelativeHumidityMeasurementCluster.java new file mode 100644 index 00000000000..5aa01d84e65 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RelativeHumidityMeasurementCluster.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * RelativeHumidityMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class RelativeHumidityMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0405; + public static final String CLUSTER_NAME = "RelativeHumidityMeasurement"; + public static final String CLUSTER_PREFIX = "relativeHumidityMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_MEASURED_VALUE = "measuredValue"; + public static final String ATTRIBUTE_MIN_MEASURED_VALUE = "minMeasuredValue"; + public static final String ATTRIBUTE_MAX_MEASURED_VALUE = "maxMeasuredValue"; + public static final String ATTRIBUTE_TOLERANCE = "tolerance"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * MeasuredValue represents the water content in % as follows: + * MeasuredValue = 100 x water content + * Where 0% < = water content < = 100%, corresponding to a MeasuredValue in the range 0 to 10000. + * The maximum resolution this format allows is 0.01%. + * MinMeasuredValue and MaxMeasuredValue define the range of the sensor. + * The null value indicates that the measurement is unknown, otherwise the range shall be as described in Measured + * Value. + * MeasuredValue is updated continuously as new measurements are made. + */ + public Integer measuredValue; // 0 uint16 R V + /** + * The MinMeasuredValue attribute indicates the minimum value of MeasuredValue that can be measured. The null value + * means this attribute is not defined. See Measured Value for more details. + */ + public Integer minMeasuredValue; // 1 uint16 R V + /** + * The MaxMeasuredValue attribute indicates the maximum value of MeasuredValue that can be measured. The null value + * means this attribute is not defined. See Measured Value for more details. + */ + public Integer maxMeasuredValue; // 2 uint16 R V + /** + * See Measured Value. + */ + public Integer tolerance; // 3 uint16 R V + + public RelativeHumidityMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1029, "RelativeHumidityMeasurement"); + } + + protected RelativeHumidityMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "measuredValue : " + measuredValue + "\n"; + str += "minMeasuredValue : " + minMeasuredValue + "\n"; + str += "maxMeasuredValue : " + maxMeasuredValue + "\n"; + str += "tolerance : " + tolerance + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ResourceMonitoringCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ResourceMonitoringCluster.java new file mode 100644 index 00000000000..41d86c61bcb --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ResourceMonitoringCluster.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ResourceMonitoring + * + * @author Dan Cunningham - Initial contribution + */ +public abstract class ResourceMonitoringCluster extends BaseCluster { + + public static final String CLUSTER_NAME = "ResourceMonitoring"; + public static final String CLUSTER_PREFIX = "resourceMonitoring"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CONDITION = "condition"; + public static final String ATTRIBUTE_DEGRADATION_DIRECTION = "degradationDirection"; + public static final String ATTRIBUTE_CHANGE_INDICATION = "changeIndication"; + public static final String ATTRIBUTE_IN_PLACE_INDICATOR = "inPlaceIndicator"; + public static final String ATTRIBUTE_LAST_CHANGED_TIME = "lastChangedTime"; + public static final String ATTRIBUTE_REPLACEMENT_PRODUCT_LIST = "replacementProductList"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the current condition of the resource in percent. + */ + public Integer condition; // 0 percent R V + /** + * Indicates the direction of change for the condition of the resource over time, which helps to determine whether a + * higher or lower condition value is considered optimal. + */ + public DegradationDirectionEnum degradationDirection; // 1 DegradationDirectionEnum R V + /** + * This attribute shall be populated with a value from ChangeIndicationEnum that is indicative of the current + * requirement to change the resource. + */ + public ChangeIndicationEnum changeIndication; // 2 ChangeIndicationEnum R V + /** + * Indicates whether a resource is currently installed. A value of true shall indicate that a resource is installed. + * A value of false shall indicate that a resource is not installed. + */ + public Boolean inPlaceIndicator; // 3 bool R V + /** + * This attribute may indicates the time at which the resource has been changed, if supported by the server. The + * attribute shall be null if it was never set or is unknown. + */ + public Integer lastChangedTime; // 4 epoch-s RW VO + /** + * Indicates the list of supported products that may be used as replacements for the current resource. Each item in + * this list represents a unique ReplacementProductStruct. + */ + public List replacementProductList; // 5 list R V + // Structs + + /** + * Indicates the product identifier that can be used as a replacement for the resource. + */ + public class ReplacementProductStruct { + public ProductIdentifierTypeEnum productIdentifierType; // ProductIdentifierTypeEnum + public String productIdentifierValue; // string + + public ReplacementProductStruct(ProductIdentifierTypeEnum productIdentifierType, + String productIdentifierValue) { + this.productIdentifierType = productIdentifierType; + this.productIdentifierValue = productIdentifierValue; + } + } + + // Enums + /** + * Indicates the direction in which the condition of the resource changes over time. + */ + public enum DegradationDirectionEnum implements MatterEnum { + UP(0, "Up"), + DOWN(1, "Down"); + + public final Integer value; + public final String label; + + private DegradationDirectionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ChangeIndicationEnum implements MatterEnum { + OK(0, "Ok"), + WARNING(1, "Warning"), + CRITICAL(2, "Critical"); + + public final Integer value; + public final String label; + + private ChangeIndicationEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * Indicate the type of identifier used to describe the product. Devices SHOULD use globally-recognized IDs over OEM + * specific ones. + */ + public enum ProductIdentifierTypeEnum implements MatterEnum { + UPC(0, "Upc"), + GTIN8(1, "Gtin 8"), + EAN(2, "Ean"), + GTIN14(3, "Gtin 14"), + OEM(4, "Oem"); + + public final Integer value; + public final String label; + + private ProductIdentifierTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Supports monitoring the condition of the resource in percentage + */ + public boolean condition; + /** + * + * Supports warning indication + */ + public boolean warning; + /** + * + * Supports specifying the list of replacement products + */ + public boolean replacementProductList; + + public FeatureMap(boolean condition, boolean warning, boolean replacementProductList) { + this.condition = condition; + this.warning = warning; + this.replacementProductList = replacementProductList; + } + } + + protected ResourceMonitoringCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, the device shall reset the Condition and ChangeIndicator attributes, indicating full resource + * availability and readiness for use, as initially configured. Invocation of this command may cause the + * LastChangedTime to be updated automatically based on the clock of the server, if the server supports setting the + * attribute. + */ + public static ClusterCommand resetCondition() { + return new ClusterCommand("resetCondition"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "condition : " + condition + "\n"; + str += "degradationDirection : " + degradationDirection + "\n"; + str += "changeIndication : " + changeIndication + "\n"; + str += "inPlaceIndicator : " + inPlaceIndicator + "\n"; + str += "lastChangedTime : " + lastChangedTime + "\n"; + str += "replacementProductList : " + replacementProductList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcCleanModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcCleanModeCluster.java new file mode 100644 index 00000000000..d8c14cc0d55 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcCleanModeCluster.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * RvcCleanMode + * + * @author Dan Cunningham - Initial contribution + */ +public class RvcCleanModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x0055; + public static final String CLUSTER_NAME = "RvcCleanMode"; + public static final String CLUSTER_PREFIX = "rvcCleanMode"; + + // Enums + public enum ModeChangeStatus implements MatterEnum { + CLEANING_IN_PROGRESS(64, "Cleaning In Progress"); + + public final Integer value; + public final String label; + + private ModeChangeStatus(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + DEEP_CLEAN(16384, "Deep Clean"), + VACUUM(16385, "Vacuum"), + MOP(16386, "Mop"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public RvcCleanModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 85, "RvcCleanMode"); + } + + protected RvcCleanModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcOperationalStateCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcOperationalStateCluster.java new file mode 100644 index 00000000000..7d17e02f8a5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcOperationalStateCluster.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * RvcOperationalState + * + * @author Dan Cunningham - Initial contribution + */ +public class RvcOperationalStateCluster extends OperationalStateCluster { + + public static final int CLUSTER_ID = 0x0061; + public static final String CLUSTER_NAME = "RvcOperationalState"; + public static final String CLUSTER_PREFIX = "rvcOperationalState"; + + // Enums + /** + * The values defined herein are applicable to this derived cluster of Operational State only and are additional to + * the set of values defined in Operational State itself. + * RVC Pause Compatibility defines the compatibility of the states this cluster defines with the Pause command. + * ### Table 13. RVC Pause Compatibility + * RVC Resume Compatibility defines the compatibility of the states this cluster defines with the Resume command. + * ### Table 14. RVC Resume Compatibility + * While in the Charging or Docked states, the device shall NOT attempt to resume unless it transitioned to those + * states while operating and can resume, such as, for example, if it is recharging while in a cleaning cycle. Else, + * if the operational state is Charging or Docked but there’s no operation to resume or the operation can’t be + * resumed, the device shall respond with an OperationalCommandResponse command with an ErrorStateID of + * CommandInvalidInState but take no further action. + */ + public enum OperationalStateEnum implements MatterEnum { + STOPPED(0, "Stopped"), + RUNNING(1, "Running"), + PAUSED(2, "Paused"), + ERROR(3, "Error"), + SEEKING_CHARGER(64, "Seeking Charger"), + CHARGING(65, "Charging"), + DOCKED(66, "Docked"); + + public final Integer value; + public final String label; + + private OperationalStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * The values defined herein are applicable to this derived cluster of Operational State only and are additional to + * the set of values defined in Operational State itself. + */ + public enum ErrorStateEnum implements MatterEnum { + NO_ERROR(0, "No Error"), + UNABLE_TO_START_OR_RESUME(1, "Unable To Start Or Resume"), + UNABLE_TO_COMPLETE_OPERATION(2, "Unable To Complete Operation"), + COMMAND_INVALID_IN_STATE(3, "Command Invalid In State"), + FAILED_TO_FIND_CHARGING_DOCK(64, "Failed To Find Charging Dock"), + STUCK(65, "Stuck"), + DUST_BIN_MISSING(66, "Dust Bin Missing"), + DUST_BIN_FULL(67, "Dust Bin Full"), + WATER_TANK_EMPTY(68, "Water Tank Empty"), + WATER_TANK_MISSING(69, "Water Tank Missing"), + WATER_TANK_LID_OPEN(70, "Water Tank Lid Open"), + MOP_CLEANING_PAD_MISSING(71, "Mop Cleaning Pad Missing"); + + public final Integer value; + public final String label; + + private ErrorStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public RvcOperationalStateCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 97, "RvcOperationalState"); + } + + protected RvcOperationalStateCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * On receipt of this command, the device shall start seeking the charging dock, if possible in the current state of + * the device. + * If this command is received when already in the SeekingCharger state the device shall respond with an + * OperationalCommandResponse command with an ErrorStateID of NoError but the command shall have no other effect. + * A device that receives this command in any state which does not allow seeking the charger, such as Charging or + * Docked, shall respond with an OperationalCommandResponse command with an ErrorStateID of CommandInvalidInState + * and shall have no other effect. + * Otherwise, on success: + * • The OperationalState attribute shall be set to SeekingCharger. + * • The device shall respond with an OperationalCommandResponse command with an ErrorStateID of NoError. + */ + public static ClusterCommand goHome() { + return new ClusterCommand("goHome"); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcRunModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcRunModeCluster.java new file mode 100644 index 00000000000..bf08c5de8e5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/RvcRunModeCluster.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * RvcRunMode + * + * @author Dan Cunningham - Initial contribution + */ +public class RvcRunModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x0054; + public static final String CLUSTER_NAME = "RvcRunMode"; + public static final String CLUSTER_PREFIX = "rvcRunMode"; + + // Enums + public enum ModeChangeStatus implements MatterEnum { + STUCK(65, "Stuck"), + DUST_BIN_MISSING(66, "Dust Bin Missing"), + DUST_BIN_FULL(67, "Dust Bin Full"), + WATER_TANK_EMPTY(68, "Water Tank Empty"), + WATER_TANK_MISSING(69, "Water Tank Missing"), + WATER_TANK_LID_OPEN(70, "Water Tank Lid Open"), + MOP_CLEANING_PAD_MISSING(71, "Mop Cleaning Pad Missing"), + BATTERY_LOW(72, "Battery Low"); + + public final Integer value; + public final String label; + + private ModeChangeStatus(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + IDLE(16384, "Idle"), + CLEANING(16385, "Cleaning"), + MAPPING(16386, "Mapping"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public RvcRunModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 84, "RvcRunMode"); + } + + protected RvcRunModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ScenesManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ScenesManagementCluster.java new file mode 100644 index 00000000000..eac150a13bb --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ScenesManagementCluster.java @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ScenesManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class ScenesManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0062; + public static final String CLUSTER_NAME = "ScenesManagement"; + public static final String CLUSTER_PREFIX = "scenesManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_LAST_CONFIGURED_BY = "lastConfiguredBy"; + public static final String ATTRIBUTE_SCENE_TABLE_SIZE = "sceneTableSize"; + public static final String ATTRIBUTE_FABRIC_SCENE_INFO = "fabricSceneInfo"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the Node ID of the node that last configured the Scene Table. + * The null value indicates that the server has not been configured, or that the identifier of the node that last + * configured the Scenes Management cluster is not known. + * The Node ID is scoped to the accessing fabric. + */ + public BigInteger lastConfiguredBy; // 0 node-id R V + /** + * Indicates the number of entries in the Scene Table on this endpoint. This is the total across all fabrics; note + * that a single fabric cannot use all those entries (see Handling of fabric-scoping). The minimum size of this + * table, (i.e., the minimum number of scenes to support across all fabrics per endpoint) shall be 16, unless a + * device type in which this cluster is used, defines a larger value in the device type definition. + */ + public Integer sceneTableSize; // 1 uint16 R V + /** + * Indicates a list of fabric scoped information about scenes on this endpoint. + * The number of list entries for this attribute shall NOT exceed the number of supported fabrics by the device. + */ + public List fabricSceneInfo; // 2 list R F V + // Structs + + public class SceneInfoStruct { + /** + * This field shall indicate the number of scenes currently used in the server’s Scene Table on the endpoint + * where the Scenes Management cluster appears. + * This only includes the count for the associated fabric. + */ + public Integer sceneCount; // uint8 + /** + * This field shall indicate the scene identifier of the scene last invoked on the associated fabric. If no + * scene has been invoked, the value of this field shall be 0xFF, the undefined scene identifier. + */ + public Integer currentScene; // uint8 + /** + * This field shall indicate the group identifier of the scene last invoked on the associated fabric, or 0 if + * the scene last invoked is not associated with a group. + */ + public Integer currentGroup; // group-id + /** + * This field shall indicate whether the state of the server corresponds to that associated with the + * CurrentScene and CurrentGroup fields of the SceneInfoStruct they belong to. TRUE indicates that these fields + * are valid, FALSE indicates that they are not valid. + * This field shall be set to False for all other fabrics when an attribute with the Scenes ("S") + * designation in the Quality column of another cluster present on the same endpoint is modified or when the + * current scene is modified by a fabric through the RecallScene or StoreScene commands, regardless of the + * fabric-scoped access quality of the command. + * In the event where the SceneValid field is set to False for a fabric, the CurrentScene and CurrentGroup + * fields shall be the last invoked scene and group for that fabric. In the event where no scene was previously + * invoked for that fabric, the CurrentScene and CurrentGroup fields shall be their default values. + */ + public Boolean sceneValid; // bool + /** + * This field shall indicate the remaining capacity of the Scene Table on this endpoint for the accessing + * fabric. Note that this value may change between reads, even if no entries are added or deleted on the + * accessing fabric, due to other clients associated with other fabrics adding or deleting entries that impact + * the resource usage on the device. + */ + public Integer remainingCapacity; // uint8 + public Integer fabricIndex; // FabricIndex + + public SceneInfoStruct(Integer sceneCount, Integer currentScene, Integer currentGroup, Boolean sceneValid, + Integer remainingCapacity, Integer fabricIndex) { + this.sceneCount = sceneCount; + this.currentScene = currentScene; + this.currentGroup = currentGroup; + this.sceneValid = sceneValid; + this.remainingCapacity = remainingCapacity; + this.fabricIndex = fabricIndex; + } + } + + /** + * This data type indicates a combination of an identifier and the value of an attribute. + */ + public class AttributeValuePairStruct { + /** + * This field shall be present for all instances in a given ExtensionFieldSetStruct. + * Which Value* field is used shall be determined based on the data type of the attribute indicated by + * AttributeID, as described in the Value* Fields subsection. + * The AttributeID field shall NOT refer to an attribute without the Scenes ("S") designation in the + * Quality column of the cluster specification. + * ### 1.4.7.3.2. ValueUnsigned8, ValueSigned8, ValueUnsigned16, ValueSigned16, ValueUnsigned32, ValueSigned32, + * ValueUnsigned64, ValueSigned64 Fields + * These fields shall indicate the attribute value as part of an extension field set, associated with a given + * AttributeID under an ExtensionFieldSetStruct’s ClusterID. Which of the fields is used shall be determined by + * the type of the attribute indicated by AttributeID as follows: + * • Data types bool, map8, and uint8 shall map to ValueUnsigned8. + * • Data types int8 shall map to ValueSigned8. + * • Data types map16 and uint16 shall map to ValueUnsigned16. + * • Data types int16 shall map to ValueSigned16. + * • Data types map32, uint24, and uint32 shall map to ValueUnsigned32. + * • Data types int24 and int32 shall map to ValueSigned32. + * • Data types map64, uint40, uint48, uint56 and uint64 shall map to ValueUnsigned64. + * • Data types int40, int48, int56 and int64 shall map to ValueSigned64. + * • For derived types, the mapping shall be based on the base type. For example, an attribute of type percent + * shall be treated as if it were of type uint8, whereas an attribute of type percent100ths shall be treated as + * if it were of type uint16. + * • For boolean nullable attributes, any value that is not 0 or 1 shall be considered to have the null value. + * • For boolean non-nullable attributes, any value that is not 0 or 1 shall be considered to have the value + * FALSE. + * • For non-boolean nullable attributes, any value that is not a valid numeric value for the attribute’s type + * after accounting for range reductions due to being nullable and constraints shall be considered to have the + * null value for the type. + * • For non-boolean non-nullable attributes, any value that is not a valid numeric value for the attribute’s + * type after accounting for constraints shall be considered to be the valid attribute value that is closest to + * the provided value. + * ◦ In the event that an invalid provided value is of equal numerical distance to the two closest valid values, + * the lowest of those values shall be considered the closest valid attribute value. + * If the used field does not match the data type of the attribute indicated by AttributeID, the + * AttributeValuePairStruct shall be considered invalid. + * Examples of processing are: + * • ColorControl cluster CurrentX (AttributeID 0x0003) has a type of uint16 and is not nullable. + * ◦ ValueUnsigned16 of 0xAB12 would be used as-is, as it is in range. + * ◦ ValueUnsigned16 of 0xFF80 is outside of the range allowed for attribute CurrentX, and would be saturated to + * the closest valid value, which is the maximum of the attribute’s constraint range: 0xFEFF. + * • LevelControl cluster CurrentLevel (AttributeID 0x0000) has a type of uint8 and is nullable. + * ◦ ValueUnsigned8 of 0xA1 would be used as-is, as it is in range. + * ◦ ValueUnsigned8 of 0xFF is outside the range allowed for nullable attribute CurrentLevel, and would be + * considered as the null value. + */ + public Integer attributeId; // attrib-id + public Integer valueUnsigned8; // uint8 + public Integer valueSigned8; // int8 + public Integer valueUnsigned16; // uint16 + public Integer valueSigned16; // int16 + public Integer valueUnsigned32; // uint32 + public Integer valueSigned32; // int32 + public BigInteger valueUnsigned64; // uint64 + public BigInteger valueSigned64; // int64 + + public AttributeValuePairStruct(Integer attributeId, Integer valueUnsigned8, Integer valueSigned8, + Integer valueUnsigned16, Integer valueSigned16, Integer valueUnsigned32, Integer valueSigned32, + BigInteger valueUnsigned64, BigInteger valueSigned64) { + this.attributeId = attributeId; + this.valueUnsigned8 = valueUnsigned8; + this.valueSigned8 = valueSigned8; + this.valueUnsigned16 = valueUnsigned16; + this.valueSigned16 = valueSigned16; + this.valueUnsigned32 = valueUnsigned32; + this.valueSigned32 = valueSigned32; + this.valueUnsigned64 = valueUnsigned64; + this.valueSigned64 = valueSigned64; + } + } + + /** + * This data type indicates for a given cluster a set of attributes and their values. + */ + public class ExtensionFieldSetStruct { + /** + * This field shall indicate the cluster-id of the cluster whose attributes are in the AttributeValueList field. + */ + public Integer clusterId; // cluster-id + /** + * This field shall indicate a set of attributes and their values which are stored as part of a scene. + * Attributes which do not have the Scenes ("S") designation in the Quality column of their cluster + * specification shall NOT be used in the AttributeValueList field. + */ + public List attributeValueList; // list + + public ExtensionFieldSetStruct(Integer clusterId, List attributeValueList) { + this.clusterId = clusterId; + this.attributeValueList = attributeValueList; + } + } + + /** + * The Scene Table is used to store information for each scene capable of being invoked on the server. Each scene is + * defined for a particular group. The Scene Table is defined here as a conceptual illustration to assist in + * understanding the underlying data to be stored when scenes are defined. Though the Scene Table is defined here + * using the data model architecture rules and format, the design is not normative. + * The Scene table is logically a list of fabric-scoped structs. The logical fields of each Scene Table entry struct + * are illustrated below. An ExtensionFieldSetStruct may be present for each Scenes-supporting cluster implemented + * on the same endpoint. + */ + public class LogicalSceneTable { + /** + * This field is the group identifier for which this scene applies, or 0 if the scene is not associated with a + * group. + */ + public Integer sceneGroupId; // group-id + /** + * This field is unique within this group, which is used to identify this scene. + */ + public Integer sceneId; // uint8 + /** + * The field is the name of the scene. + * If scene names are not supported, any commands that write a scene name shall simply discard the name, and any + * command that returns a scene name shall return an empty string. + */ + public String sceneName; // string + /** + * This field is the amount of time, in milliseconds, it will take for a cluster to change from its current + * state to the requested state. + */ + public Integer sceneTransitionTime; // uint32 + /** + * See the Scene Table Extensions subsections of individual clusters. A Scene Table Extension shall only use + * attributes with the Scene quality. Each ExtensionFieldSetStruct holds a set of values of these attributes for + * a cluster implemented on the same endpoint where the Scene ("S") designation appears in the quality + * column. A scene is the aggregate of all such fields across all clusters on the endpoint. + */ + public List extensionFields; // list + + public LogicalSceneTable(Integer sceneGroupId, Integer sceneId, String sceneName, Integer sceneTransitionTime, + List extensionFields) { + this.sceneGroupId = sceneGroupId; + this.sceneId = sceneId; + this.sceneName = sceneName; + this.sceneTransitionTime = sceneTransitionTime; + this.extensionFields = extensionFields; + } + } + + // Bitmaps + public static class CopyModeBitmap { + public boolean copyAllScenes; + + public CopyModeBitmap(boolean copyAllScenes) { + this.copyAllScenes = copyAllScenes; + } + } + + public static class FeatureMap { + /** + * + * This feature indicates the ability to store a name for a scene when a scene is added. + */ + public boolean sceneNames; + + public FeatureMap(boolean sceneNames) { + this.sceneNames = sceneNames; + } + } + + public ScenesManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 98, "ScenesManagement"); + } + + protected ScenesManagementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * It is not mandatory for an extension field set to be included in the command for every cluster on that endpoint + * that has a defined extension field set. Extension field sets may be omitted, including the case of no extension + * field sets at all. + */ + public static ClusterCommand addScene(Integer groupId, Integer sceneId, Integer transitionTime, String sceneName, + List extensionFieldSetStructs) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (sceneId != null) { + map.put("sceneId", sceneId); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + if (sceneName != null) { + map.put("sceneName", sceneName); + } + if (extensionFieldSetStructs != null) { + map.put("extensionFieldSetStructs", extensionFieldSetStructs); + } + return new ClusterCommand("addScene", map); + } + + public static ClusterCommand viewScene(Integer groupId, Integer sceneId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (sceneId != null) { + map.put("sceneId", sceneId); + } + return new ClusterCommand("viewScene", map); + } + + public static ClusterCommand removeScene(Integer groupId, Integer sceneId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (sceneId != null) { + map.put("sceneId", sceneId); + } + return new ClusterCommand("removeScene", map); + } + + public static ClusterCommand removeAllScenes(Integer groupId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + return new ClusterCommand("removeAllScenes", map); + } + + public static ClusterCommand storeScene(Integer groupId, Integer sceneId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (sceneId != null) { + map.put("sceneId", sceneId); + } + return new ClusterCommand("storeScene", map); + } + + public static ClusterCommand recallScene(Integer groupId, Integer sceneId, Integer transitionTime) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + if (sceneId != null) { + map.put("sceneId", sceneId); + } + if (transitionTime != null) { + map.put("transitionTime", transitionTime); + } + return new ClusterCommand("recallScene", map); + } + + /** + * This command can be used to get the used scene identifiers within a certain group, for the endpoint that + * implements this cluster. + */ + public static ClusterCommand getSceneMembership(Integer groupId) { + Map map = new LinkedHashMap<>(); + if (groupId != null) { + map.put("groupId", groupId); + } + return new ClusterCommand("getSceneMembership", map); + } + + /** + * This command allows a client to efficiently copy scenes from one group/scene identifier pair to another + * group/scene identifier pair. + */ + public static ClusterCommand copyScene(CopyModeBitmap mode, Integer groupIdentifierFrom, + Integer sceneIdentifierFrom, Integer groupIdentifierTo, Integer sceneIdentifierTo) { + Map map = new LinkedHashMap<>(); + if (mode != null) { + map.put("mode", mode); + } + if (groupIdentifierFrom != null) { + map.put("groupIdentifierFrom", groupIdentifierFrom); + } + if (sceneIdentifierFrom != null) { + map.put("sceneIdentifierFrom", sceneIdentifierFrom); + } + if (groupIdentifierTo != null) { + map.put("groupIdentifierTo", groupIdentifierTo); + } + if (sceneIdentifierTo != null) { + map.put("sceneIdentifierTo", sceneIdentifierTo); + } + return new ClusterCommand("copyScene", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "lastConfiguredBy : " + lastConfiguredBy + "\n"; + str += "sceneTableSize : " + sceneTableSize + "\n"; + str += "fabricSceneInfo : " + fabricSceneInfo + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ServiceAreaCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ServiceAreaCluster.java new file mode 100644 index 00000000000..1cdb782c6b7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ServiceAreaCluster.java @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ServiceArea + * + * @author Dan Cunningham - Initial contribution + */ +public class ServiceAreaCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0150; + public static final String CLUSTER_NAME = "ServiceArea"; + public static final String CLUSTER_PREFIX = "serviceArea"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_SUPPORTED_AREAS = "supportedAreas"; + public static final String ATTRIBUTE_SUPPORTED_MAPS = "supportedMaps"; + public static final String ATTRIBUTE_SELECTED_AREAS = "selectedAreas"; + public static final String ATTRIBUTE_CURRENT_AREA = "currentArea"; + public static final String ATTRIBUTE_ESTIMATED_END_TIME = "estimatedEndTime"; + public static final String ATTRIBUTE_PROGRESS = "progress"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall contain the list of areas that can be included in the SelectedAreas attribute’s list. Each + * item in this list represents a unique area, as indicated by the AreaID field of AreaStruct. + * Each entry in this list shall have a unique value for the AreaID field. + * If the SupportedMaps attribute is not empty, each entry in this list shall have a unique value for the + * combination of the MapID and AreaInfo fields. + * If the SupportedMaps attribute is empty, each entry in this list shall have a unique value for the AreaInfo field + * and shall have the MapID field set to null. + * An empty value indicates that the device is currently unable to provide the list of supported areas. + * > [!NOTE] + * > due to the maximum size of this list and to the fact that the entries may include strings (see + * LocationName), care must be taken by implementers to avoid creating a data structure that is overly large, which + * can result in significant latency in accessing this attribute. + * The value of this attribute may change at any time via an out-of-band interaction outside of the server, such as + * interactions with a user interface, or due to internal device changes. + * When removing entries in the SupportedAreas attribute list the server shall adjust the values of the + * SelectedAreas, CurrentArea, and Progress attributes such that they only reference valid entries in the updated + * SupportedAreas attribute list. These changes to the SelectedAreas, CurrentArea, and Progress attributes may + * result in the server setting some or all of them to empty (for SelectedAreas and Progress) or null (for + * CurrentArea), or updating them with data that matches the constraints from the description of the respective + * attributes. These actions are required to ensure having a consistent representation of the maps and locations + * available to the clients. + * The SupportedAreas attribute list changes mentioned above SHOULD NOT be allowed while the device is operating, to + * reduce the impact on the clients, and the potential confusion for the users. + * A few examples are provided below. Valid list of areas: + * • AreaID=0, LocationName="yellow bedroom", MapID=null + * • AreaID=1, LocationName="orange bedroom", MapID=null Valid list of areas: + * • AreaID=5, LocationName="hallway", MapID=1 + * • AreaID=3, LocationName="hallway", MapID=2 + */ + public List supportedAreas; // 0 list R V + /** + * This attribute shall contain the list of supported maps. + * A map is a full or a partial representation of a home, known to the device. For example: + * • a single level home may be represented using a single map + * • a two level home may be represented using two maps, one for each level + * • a single level home may be represented using two maps, each including a different set of rooms, such as + * "map of living room and kitchen" and "map of bedrooms and hallway" + * • a single level home may be represented using one map for the indoor areas (living room, bedrooms etc.) and one + * for the outdoor areas (garden, swimming pool etc.) + * Each map includes one or more areas - see the SupportedAreas attribute. In the context of this cluster + * specification, a map is effectively a group label for a set of areas, rather than a graphical representation that + * the clients can display to the users. The clients that present the list of available areas for user selection + * (see the SelectAreas command) may choose to filter the SupportedAreas list based on the associated map. For + * example, the clients may allow the user to indicate that the device is to operate on the first floor, and allow + * the user to choose only from the areas situated on that level. + * If empty, that indicates that the device is currently unable to provide this information. Each entry in this list + * shall have a unique value for the MapID field. + * Each entry in this list shall have a unique value for the Name field. + * > [!NOTE] + * > due to the maximum size of this list and to the fact that the entries may include strings (see the Name + * field of the MapStruct data type), care must be taken by implementers to avoid creating a data structure that is + * overly large, which can result in significant latency in accessing this attribute. + * The value of this attribute may change at any time via an out-of-band interaction outside of the server, such as + * interactions with a user interface. + * When updating the SupportedMaps attribute list by deleting entries, or by setting the attribute to an empty list, + * the SupportedLocations attribute shall be updated such that all entries in that list meet the constraints + * indicated in the description of the SupportedLocations attribute. This may result in the server removing entries + * from the SupportedAreas attribute list. See the SupportedAreas attribute description for the implications of + * changing that attribute. + * The SupportedMaps attribute list changes mentioned above SHOULD NOT be allowed while the device is operating, to + * reduce the impact on the clients, and the potential confusion for the users. + */ + public List supportedMaps; // 1 list R V + /** + * Indicates the set of areas where the device SHOULD attempt to operate. + * The mobile devices may travel without operating across any areas while attempting to reach the areas indicated by + * the SelectedAreas attribute. For example, a robotic vacuum cleaner may drive without cleaning when traveling + * without operating. + * If this attribute is empty, the device is not constrained to operate in any specific areas. If this attribute is + * not empty: + * • each item in this list shall match the AreaID field of an entry in the SupportedAreas attribute’s list + * • each entry in this list shall have a unique value + */ + public List selectedAreas; // 2 list R V + /** + * If the device is mobile, this attribute shall indicate the area where the device is currently located, regardless + * of whether it is operating or not, such as while traveling between areas. + * If the device is not mobile and can operate at multiple areas sequentially, this attribute shall indicate the + * area which is currently being serviced, or the area which is currently traversed by the device. For example, a + * camera device may use this attribute to indicate which area it currently takes video of (serviced area) or which + * area it currently has in view but not taking video of (e.g. an area which is traversed while panning). + * > [!NOTE] + * > A device may traverse an area regardless of the status of the area (pending, skipped, or completed). + * If a device can simultaneously operate at multiple areas, such as in the case of a sensor that can monitor + * multiple areas at the same time, the CurrentArea attribute shall NOT be implemented, since it doesn’t apply. Else + * this attribute shall be optionally implemented. + * A null value indicates that the device is currently unable to provide this information. For example, the device + * is traversing an unknown area, or the SupportedAreas attribute was updated and the area where the device is + * located was removed from that list. + * If not null, the value of this attribute shall match the AreaID field of an entry on the SupportedAreas + * attribute’s list. + */ + public Integer currentArea; // 3 uint32 R V + /** + * Indicates the estimated Epoch time for completing operating at the area indicated by the CurrentArea attribute, + * in seconds. + * A value of 0 means that the operation has completed. + * When this attribute is null, that represents that there is no time currently defined until operation completion. + * This may happen, for example, because no operation is in progress or because the completion time is unknown. + * Null if the CurrentArea attribute is null. + * If the Progress attribute is available, and it contains an entry matching CurrentArea, the server may use the + * time estimate provided in the InitialTimeEstimate field of that entry to compute the EstimatedEndTime attribute. + * The value of this attribute shall only be reported in the following cases: + * • when it changes to or from 0 + * • when it decreases + * • when it changes to or from null + * > [!NOTE] + * > If the device is capable of pausing its operation, this attribute may be set to null, to indicate that + * completion time is unknown, or increment the value while being in the paused state. + */ + public Integer estimatedEndTime; // 4 epoch-s R V + /** + * Indicates the operating status at one or more areas. Each entry in this list shall have a unique value for the + * AreaID field. + * For each entry in this list, the AreaID field shall match an entry on the SupportedAreas attribute’s list. + * When this attribute is empty, that represents that no progress information is currently available. + * If the SelectedAreas attribute is empty, indicating the device is not constrained to operate in any specific + * areas, the Progress attribute list may change while the device operates, due to the device adding new entries + * dynamically, when it determines which ones it can attempt to operate at. + * If the SelectedAreas attribute is not empty, and the device starts operating: + * • the Progress attribute list shall be updated so each entry of SelectedAreas has a matching Progress list entry, + * based on the AreaID field + * • the length of the Progress and SelectedAreas list shall be the same + * • the entries in the Progress list shall be initialized by the server, by having their status set to Pending or + * Operating, and the TotalOperationalTime field set to null + * When the device ends operation unexpectedly, such as due to an error, the server shall update all Progress list + * entries with the Status field set to Operating or Pending to Skipped. + * When the device finishes operating, successfully or not, it shall NOT change the Progress attribute, except in + * the case of an unexpected end of operation as described above, or due to changes to the SupportedMaps or + * SupportedAreas attributes, so the clients can retrieve the progress information at that time. + * > [!NOTE] + * > if the device implements the Operational Status cluster, or a derivation of it, in case the device fails to + * service any locations in the SelectedAreas list before ending the operation, it SHOULD use the Operational Status + * cluster to indicate that the device was unable to complete the operation (see the UnableToCompleteOperation error + * from that cluster specification). The clients SHOULD then read the Progress attribute, and indicate which areas + * have been successfully serviced (marked as completed). + */ + public List progress; // 5 list R V + // Structs + + /** + * The data from this structure indicates a landmark and position relative to the landmark. + */ + public class LandmarkInfoStruct { + /** + * This field shall indicate that the area is associated with a landmark. + * This field shall be the ID of a landmark semantic tag, located within the Common Landmark Namespace. For + * example, this tag may indicate that the area refers to an area next to a table. + */ + public Integer landmarkTag; // tag + /** + * This field shall identify the position of the area relative to a landmark. This is a static description of a + * zone known to the server, and this field never reflects the device’s own proximity or position relative to + * the landmark, but that of the zone. + * This field shall be the ID of a relative position semantic tag, located within the Common Relative Position + * Namespace. + * If the RelativePositionTag field is null, this field indicates proximity to the landmark. Otherwise, the + * RelativePositionTag field indicates the position of the area relative to the landmark indicated by the + * LandmarkTag field. For example, this tag, in conjunction with the LandmarkTag field, may indicate that the + * area refers to a zone under a table. + */ + public Integer relativePositionTag; // tag + + public LandmarkInfoStruct(Integer landmarkTag, Integer relativePositionTag) { + this.landmarkTag = landmarkTag; + this.relativePositionTag = relativePositionTag; + } + } + + /** + * The data from this structure indicates the name and/or semantic data describing an area, as detailed below. + * This data type includes the LocationInfo field, with the following fields: LocationName, FloorNumber, AreaType. + * Additional semantic data may be available in the LandmarkInfo field. + * For an area description to be meaningful, it shall have at least one of the following: + * • a non-empty name (LocationInfo’s LocationName field) OR + * • some semantic data (one or more of these: FloorNumber, AreaType or LandmarkTag) The normative text from the + * remainder of this section describes these constraints. + * If the LocationInfo field is null, the LandmarkInfo field shall NOT be null. If the LandmarkInfo field is null, + * the LocationInfo field shall NOT be null. + * If LocationInfo is not null, and its LocationName field is an empty string, at least one of the following shall + * NOT be null: + * • LocationInfo’s FloorNumber field + * • LocationInfo’s AreaType field + * • LandmarkInfo field + * If all three of the following are null, LocationInfo’s LocationName field shall NOT be an empty string: + * • LocationInfo’s FloorNumber field + * • LocationInfo’s AreaType field + * • LandmarkInfo field + */ + public class AreaInfoStruct { + /** + * This field shall indicate the name of the area, floor number and/or area type. A few examples are provided + * below. + * • An area can have LocationInfo’s LocationName field set to "blue room", and the AreaType field set + * to the ID of a "Living Room" semantic tag. Clients wishing to direct the device to operate in (or + * service) the living room can use this area. + * • An area can have LocationInfo set to null, the LandmarkInfo’s LandmarkTag field set to the ID of the + * "Table" landmark semantic tag, and the RelativePositionTag field set to the ID of the + * "Under" position semantic tag. With such an area indication, the client can request the device to + * operate in (or service) the area located under the table. + */ + public String locationInfo; // locationdesc + /** + * This field shall indicate an association with a landmark. A value of null indicates that the information is + * not available or known. For example, this may indicate that the area refers to a zone next to a table. + * If this field is not null, that indicates that the area is restricted to the zone where the landmark is + * located, as indicated by the LandmarkTag and, if not null, by the RelativePositionTag fields, rather than to + * the entire room or floor where the landmark is located, if those are indicated by the LocationInfo field. + */ + public LandmarkInfoStruct landmarkInfo; // LandmarkInfoStruct + + public AreaInfoStruct(String locationInfo, LandmarkInfoStruct landmarkInfo) { + this.locationInfo = locationInfo; + this.landmarkInfo = landmarkInfo; + } + } + + /** + * This is a struct representing a map. + */ + public class MapStruct { + /** + * This field shall represent the map’s identifier. + */ + public Integer mapId; // uint32 + /** + * This field shall represent a human understandable map description. For example: "Main Floor", or + * "Second Level". + */ + public String name; // string + + public MapStruct(Integer mapId, String name) { + this.mapId = mapId; + this.name = name; + } + } + + /** + * This is a struct representing an area known to the server. + */ + public class AreaStruct { + /** + * This field shall represent the identifier of the area. + */ + public Integer areaId; // uint32 + /** + * This field shall indicate the map identifier which the area is associated with. A value of null indicates + * that the area is not associated with a map. + * If the SupportedMaps attribute is not empty, this field shall match the MapID field of an entry from the + * SupportedMaps attribute’s list. If the SupportedMaps attribute is empty, this field shall be null. + */ + public Integer mapId; // uint32 + /** + * This field shall contain data describing the area. + * This SHOULD be used by clients to determine the name and/or the full, or the partial, semantics of a certain + * area. + * > [!NOTE] + * > If any entries on the SupportedAreas attribute’s list have the AreaInfo field missing the semantic data, + * the client may remind the user to assign the respective data. + */ + public AreaInfoStruct areaInfo; // AreaInfoStruct + + public AreaStruct(Integer areaId, Integer mapId, AreaInfoStruct areaInfo) { + this.areaId = areaId; + this.mapId = mapId; + this.areaInfo = areaInfo; + } + } + + /** + * This is a struct indicating the progress. + */ + public class ProgressStruct { + /** + * This field shall indicate the identifier of the area, and the identifier shall be an entry in the + * SupportedAreas attribute’s list. + */ + public Integer areaId; // uint32 + /** + * This field shall indicate the operational status of the device regarding the area indicated by the AreaID + * field. + */ + public OperationalStatusEnum status; // OperationalStatusEnum + /** + * This field shall indicate the total operational time, in seconds, from when the device started to operate at + * the area indicated by the AreaID field, until the operation finished, due to completion or due to skipping, + * including any time spent while paused. + * A value of null indicates that the total operational time is unknown. + * There may be cases where the total operational time exceeds the maximum value that can be conveyed by this + * attribute, and in such instances this attribute shall be populated with null. Null if the Status field is not + * set to Completed or Skipped. + */ + public Integer totalOperationalTime; // elapsed-s + /** + * This field shall indicate the estimated time for the operation, in seconds, from when the device will start + * operating at the area indicated by the AreaID field, until the operation completes, excluding any time spent + * while not operating in the area. + * A value of null indicates that the estimated time is unknown. If the estimated time is unknown, or if it + * exceeds the maximum value that can be conveyed by this attribute, this attribute shall be null. + * After initializing the ProgressStruct instance, the server SHOULD NOT change the value of this field, except + * when repopulating the entire instance, to avoid excessive reporting of the Progress attribute changes. + */ + public Integer estimatedTime; // elapsed-s + + public ProgressStruct(Integer areaId, OperationalStatusEnum status, Integer totalOperationalTime, + Integer estimatedTime) { + this.areaId = areaId; + this.status = status; + this.totalOperationalTime = totalOperationalTime; + this.estimatedTime = estimatedTime; + } + } + + // Enums + /** + * The following table defines the status values. + */ + public enum OperationalStatusEnum implements MatterEnum { + PENDING(0, "Pending"), + OPERATING(1, "Operating"), + SKIPPED(2, "Skipped"), + COMPLETED(3, "Completed"); + + public final Integer value; + public final String label; + + private OperationalStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SelectAreasStatus implements MatterEnum { + SUCCESS(0, "Success"), + UNSUPPORTED_AREA(1, "Unsupported Area"), + INVALID_IN_MODE(2, "Invalid In Mode"), + INVALID_SET(3, "Invalid Set"); + + public final Integer value; + public final String label; + + private SelectAreasStatus(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SkipAreaStatus implements MatterEnum { + SUCCESS(0, "Success"), + INVALID_AREA_LIST(1, "Invalid Area List"), + INVALID_IN_MODE(2, "Invalid In Mode"), + INVALID_SKIPPED_AREA(3, "Invalid Skipped Area"); + + public final Integer value; + public final String label; + + private SkipAreaStatus(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature indicates whether this device allows changing the selected areas, by using the SelectAreas + * command, while operating. + */ + public boolean selectWhileRunning; + /** + * + * The device implements the progress reporting feature + */ + public boolean progressReporting; + /** + * + * The device has map support + */ + public boolean maps; + + public FeatureMap(boolean selectWhileRunning, boolean progressReporting, boolean maps) { + this.selectWhileRunning = selectWhileRunning; + this.progressReporting = progressReporting; + this.maps = maps; + } + } + + public ServiceAreaCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 336, "ServiceArea"); + } + + protected ServiceAreaCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used to select a set of device areas, where the device is to operate. + * On receipt of this command the device shall respond with a SelectAreasResponse command. + */ + public static ClusterCommand selectAreas(List newAreas) { + Map map = new LinkedHashMap<>(); + if (newAreas != null) { + map.put("newAreas", newAreas); + } + return new ClusterCommand("selectAreas", map); + } + + /** + * This command is used to skip the given area, and to attempt operating at other areas on the SupportedAreas + * attribute list. + * This command shall NOT be implemented if the CurrentArea attribute and the Progress attribute are both not + * implemented. Else, this command shall be optionally implemented. + * On receipt of this command the device shall respond with a SkipAreaResponse command. + */ + public static ClusterCommand skipArea(Integer skippedArea) { + Map map = new LinkedHashMap<>(); + if (skippedArea != null) { + map.put("skippedArea", skippedArea); + } + return new ClusterCommand("skipArea", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "supportedAreas : " + supportedAreas + "\n"; + str += "supportedMaps : " + supportedMaps + "\n"; + str += "selectedAreas : " + selectedAreas + "\n"; + str += "currentArea : " + currentArea + "\n"; + str += "estimatedEndTime : " + estimatedEndTime + "\n"; + str += "progress : " + progress + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SmokeCoAlarmCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SmokeCoAlarmCluster.java new file mode 100644 index 00000000000..88fbfba1fef --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SmokeCoAlarmCluster.java @@ -0,0 +1,461 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * SmokeCoAlarm + * + * @author Dan Cunningham - Initial contribution + */ +public class SmokeCoAlarmCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x005C; + public static final String CLUSTER_NAME = "SmokeCoAlarm"; + public static final String CLUSTER_PREFIX = "smokeCoAlarm"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_EXPRESSED_STATE = "expressedState"; + public static final String ATTRIBUTE_SMOKE_STATE = "smokeState"; + public static final String ATTRIBUTE_CO_STATE = "coState"; + public static final String ATTRIBUTE_BATTERY_ALERT = "batteryAlert"; + public static final String ATTRIBUTE_DEVICE_MUTED = "deviceMuted"; + public static final String ATTRIBUTE_TEST_IN_PROGRESS = "testInProgress"; + public static final String ATTRIBUTE_HARDWARE_FAULT_ALERT = "hardwareFaultAlert"; + public static final String ATTRIBUTE_END_OF_SERVICE_ALERT = "endOfServiceAlert"; + public static final String ATTRIBUTE_INTERCONNECT_SMOKE_ALARM = "interconnectSmokeAlarm"; + public static final String ATTRIBUTE_INTERCONNECT_CO_ALARM = "interconnectCoAlarm"; + public static final String ATTRIBUTE_CONTAMINATION_STATE = "contaminationState"; + public static final String ATTRIBUTE_SMOKE_SENSITIVITY_LEVEL = "smokeSensitivityLevel"; + public static final String ATTRIBUTE_EXPIRY_DATE = "expiryDate"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the visibly- and audibly-expressed state of the alarm. When multiple alarm conditions are being + * reflected in the server, this attribute shall indicate the condition with the highest priority. Priority order of + * conditions is determined by the manufacturer and shall be supplied as a part of certification procedure. If the + * value of ExpressedState is not Normal, the attribute corresponding to the value shall NOT be Normal. For example, + * if the ExpressedState is set to SmokeAlarm, the value of the SmokeState will indicate the severity of the alarm + * (Warning or Critical). Clients SHOULD also read the other attributes to be aware of further alarm conditions + * beyond the one indicated in ExpressedState. + * Visible expression is typically a LED light pattern. Audible expression is a horn or speaker pattern. Audible + * expression shall BE suppressed if the DeviceMuted attribute is supported and set to Muted. + */ + public ExpressedStateEnum expressedState; // 0 ExpressedStateEnum R V + /** + * Indicates whether the device’s smoke sensor is currently triggering a smoke alarm. + */ + public AlarmStateEnum smokeState; // 1 AlarmStateEnum R V + /** + * Indicates whether the device’s CO sensor is currently triggering a CO alarm. + */ + public AlarmStateEnum coState; // 2 AlarmStateEnum R V + /** + * Indicates whether the power resource fault detection mechanism is currently triggered at the device. If the + * detection mechanism is triggered, this attribute shall be set to Warning or Critical, otherwise it shall be set + * to Normal. The battery state shall also be reflected in the Power Source cluster representing the device’s + * battery using the appropriate supported attributes and events. + */ + public AlarmStateEnum batteryAlert; // 3 AlarmStateEnum R V + /** + * Indicates the whether the audible expression of the device is currently muted. Audible expression is typically a + * horn or speaker pattern. + */ + public MuteStateEnum deviceMuted; // 4 MuteStateEnum R V + /** + * Indicates whether the device self-test is currently activated. If the device self-test is activated, this + * attribute shall be set to True, otherwise it shall be set to False. + */ + public Boolean testInProgress; // 5 bool R V + /** + * Indicates whether the hardware fault detection mechanism is currently triggered. If the detection mechanism is + * triggered, this attribute shall be set to True, otherwise it shall be set to False. + */ + public Boolean hardwareFaultAlert; // 6 bool R V + /** + * Indicates whether the end-of-service has been triggered at the device. This attribute shall be set to Expired + * when the device reaches the end-of-service. + */ + public EndOfServiceEnum endOfServiceAlert; // 7 EndOfServiceEnum R V + /** + * Indicates whether the interconnected smoke alarm is currently triggering by branching devices. When the + * interconnected smoke alarm is being triggered, this attribute shall be set to Warning or Critical, otherwise it + * shall be set to Normal. + */ + public AlarmStateEnum interconnectSmokeAlarm; // 8 AlarmStateEnum R V + /** + * Indicates whether the interconnected CO alarm is currently triggering by branching devices. When the + * interconnected CO alarm is being triggered, this attribute shall be set to Warning or Critical, otherwise it + * shall be set to Normal. + */ + public AlarmStateEnum interconnectCoAlarm; // 9 AlarmStateEnum R V + /** + * Indicates the contamination level of the smoke sensor. + */ + public ContaminationStateEnum contaminationState; // 10 ContaminationStateEnum R V + /** + * Indicates the sensitivity level of the smoke sensor configured on the device. + */ + public SensitivityEnum smokeSensitivityLevel; // 11 SensitivityEnum RW VM + /** + * Indicates the date when the device reaches its stated expiry date. After the ExpiryDate has been reached, the + * EndOfServiceAlert shall start to be triggered. To account for better customer experience across time zones, the + * EndOfServiceAlert may be delayed by up to 24 hours after the ExpiryDate. Similarly, clients may delay any actions + * based on the ExpiryDate by up to 24 hours to best align with the local time zone. + */ + public Integer expiryDate; // 12 epoch-s R V + // Structs + + /** + * This event shall be generated when SmokeState attribute changes to either Warning or Critical state. + */ + public class SmokeAlarm { + /** + * This field shall indicate the current value of the SmokeState attribute. + */ + public AlarmStateEnum alarmSeverityLevel; // AlarmStateEnum + + public SmokeAlarm(AlarmStateEnum alarmSeverityLevel) { + this.alarmSeverityLevel = alarmSeverityLevel; + } + } + + /** + * This event shall be generated when COState attribute changes to either Warning or Critical state. + */ + public class CoAlarm { + /** + * This field shall indicate the current value of the COState attribute. + */ + public AlarmStateEnum alarmSeverityLevel; // AlarmStateEnum + + public CoAlarm(AlarmStateEnum alarmSeverityLevel) { + this.alarmSeverityLevel = alarmSeverityLevel; + } + } + + /** + * This event shall be generated when BatteryAlert attribute changes to either Warning or Critical state. + */ + public class LowBattery { + /** + * This field shall indicate the current value of the BatteryAlert attribute. + */ + public AlarmStateEnum alarmSeverityLevel; // AlarmStateEnum + + public LowBattery(AlarmStateEnum alarmSeverityLevel) { + this.alarmSeverityLevel = alarmSeverityLevel; + } + } + + /** + * This event shall be generated when the device detects a hardware fault that leads to setting HardwareFaultAlert + * to True. + */ + public class HardwareFault { + public HardwareFault() { + } + } + + /** + * This event shall be generated when the EndOfServiceAlert is set to Expired. + */ + public class EndOfService { + public EndOfService() { + } + } + + /** + * This event shall be generated when the SelfTest completes, and the attribute TestInProgress changes to False. + */ + public class SelfTestComplete { + public SelfTestComplete() { + } + } + + /** + * This event shall be generated when the DeviceMuted attribute changes to Muted. + */ + public class AlarmMuted { + public AlarmMuted() { + } + } + + /** + * This event shall be generated when DeviceMuted attribute changes to NotMuted. + */ + public class MuteEnded { + public MuteEnded() { + } + } + + /** + * This event shall be generated when the device hosting the server receives a smoke alarm from an interconnected + * sensor. + */ + public class InterconnectSmokeAlarm { + /** + * This field shall indicate the current value of the InterconnectSmokeAlarm attribute. + */ + public AlarmStateEnum alarmSeverityLevel; // AlarmStateEnum + + public InterconnectSmokeAlarm(AlarmStateEnum alarmSeverityLevel) { + this.alarmSeverityLevel = alarmSeverityLevel; + } + } + + /** + * This event shall be generated when the device hosting the server receives a CO alarm from an interconnected + * sensor. + */ + public class InterconnectCoAlarm { + /** + * This field shall indicate the current value of the InterconnectCOAlarm attribute. + */ + public AlarmStateEnum alarmSeverityLevel; // AlarmStateEnum + + public InterconnectCoAlarm(AlarmStateEnum alarmSeverityLevel) { + this.alarmSeverityLevel = alarmSeverityLevel; + } + } + + /** + * This event shall be generated when ExpressedState attribute returns to Normal state. + */ + public class AllClear { + public AllClear() { + } + } + + // Enums + public enum AlarmStateEnum implements MatterEnum { + NORMAL(0, "Normal"), + WARNING(1, "Warning"), + CRITICAL(2, "Critical"); + + public final Integer value; + public final String label; + + private AlarmStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SensitivityEnum implements MatterEnum { + HIGH(0, "High"), + STANDARD(1, "Standard"), + LOW(2, "Low"); + + public final Integer value; + public final String label; + + private SensitivityEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ExpressedStateEnum implements MatterEnum { + NORMAL(0, "Normal"), + SMOKE_ALARM(1, "Smoke Alarm"), + CO_ALARM(2, "Co Alarm"), + BATTERY_ALERT(3, "Battery Alert"), + TESTING(4, "Testing"), + HARDWARE_FAULT(5, "Hardware Fault"), + END_OF_SERVICE(6, "End Of Service"), + INTERCONNECT_SMOKE(7, "Interconnect Smoke"), + INTERCONNECT_CO(8, "Interconnect Co"); + + public final Integer value; + public final String label; + + private ExpressedStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum MuteStateEnum implements MatterEnum { + NOT_MUTED(0, "Not Muted"), + MUTED(1, "Muted"); + + public final Integer value; + public final String label; + + private MuteStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EndOfServiceEnum implements MatterEnum { + NORMAL(0, "Normal"), + EXPIRED(1, "Expired"); + + public final Integer value; + public final String label; + + private EndOfServiceEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ContaminationStateEnum implements MatterEnum { + NORMAL(0, "Normal"), + LOW(1, "Low"), + WARNING(2, "Warning"), + CRITICAL(3, "Critical"); + + public final Integer value; + public final String label; + + private ContaminationStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Supports Smoke alarm + */ + public boolean smokeAlarm; + /** + * + * Supports CO alarm + */ + public boolean coAlarm; + + public FeatureMap(boolean smokeAlarm, boolean coAlarm) { + this.smokeAlarm = smokeAlarm; + this.coAlarm = coAlarm; + } + } + + public SmokeCoAlarmCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 92, "SmokeCoAlarm"); + } + + protected SmokeCoAlarmCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall initiate a device self-test. The return status shall indicate whether the test was + * successfully initiated. Only one SelfTestRequest may be processed at a time. When the value of the ExpressedState + * attribute is any of SmokeAlarm, COAlarm, Testing, InterconnectSmoke, InterconnectCO, the device shall NOT execute + * the self-test, and shall return status code BUSY. + * Upon successful acceptance of SelfTestRequest, the TestInProgress attribute shall be set to True and + * ExpressedState attribute shall be set to Testing. Any faults identified during the test shall be reflected in the + * appropriate attributes and events. Upon completion of the self test procedure, the SelfTestComplete event shall + * be generated, the TestInProgress attribute shall be set to False and ExpressedState attribute shall be updated to + * reflect the current state of the server. + */ + public static ClusterCommand selfTestRequest() { + return new ClusterCommand("selfTestRequest"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "expressedState : " + expressedState + "\n"; + str += "smokeState : " + smokeState + "\n"; + str += "coState : " + coState + "\n"; + str += "batteryAlert : " + batteryAlert + "\n"; + str += "deviceMuted : " + deviceMuted + "\n"; + str += "testInProgress : " + testInProgress + "\n"; + str += "hardwareFaultAlert : " + hardwareFaultAlert + "\n"; + str += "endOfServiceAlert : " + endOfServiceAlert + "\n"; + str += "interconnectSmokeAlarm : " + interconnectSmokeAlarm + "\n"; + str += "interconnectCoAlarm : " + interconnectCoAlarm + "\n"; + str += "contaminationState : " + contaminationState + "\n"; + str += "smokeSensitivityLevel : " + smokeSensitivityLevel + "\n"; + str += "expiryDate : " + expiryDate + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SoftwareDiagnosticsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SoftwareDiagnosticsCluster.java new file mode 100644 index 00000000000..68193ee13ae --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SoftwareDiagnosticsCluster.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * SoftwareDiagnostics + * + * @author Dan Cunningham - Initial contribution + */ +public class SoftwareDiagnosticsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0034; + public static final String CLUSTER_NAME = "SoftwareDiagnostics"; + public static final String CLUSTER_PREFIX = "softwareDiagnostics"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_THREAD_METRICS = "threadMetrics"; + public static final String ATTRIBUTE_CURRENT_HEAP_FREE = "currentHeapFree"; + public static final String ATTRIBUTE_CURRENT_HEAP_USED = "currentHeapUsed"; + public static final String ATTRIBUTE_CURRENT_HEAP_HIGH_WATERMARK = "currentHeapHighWatermark"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The ThreadMetrics attribute shall be a list of ThreadMetricsStruct structs. Each active thread on the Node shall + * be represented by a single entry within the ThreadMetrics attribute. + */ + public List threadMetrics; // 0 list R V + /** + * The CurrentHeapFree attribute shall indicate the current amount of heap memory, in bytes, that are free for + * allocation. The effective amount may be smaller due to heap fragmentation or other reasons. + */ + public BigInteger currentHeapFree; // 1 uint64 R V + /** + * The CurrentHeapUsed attribute shall indicate the current amount of heap memory, in bytes, that is being used. + */ + public BigInteger currentHeapUsed; // 2 uint64 R V + /** + * The CurrentHeapHighWatermark attribute shall indicate the maximum amount of heap memory, in bytes, that has been + * used by the Node. This value shall only be reset upon a Node reboot or upon receiving of the ResetWatermarks + * command. + */ + public BigInteger currentHeapHighWatermark; // 3 uint64 R V + // Structs + + /** + * The SoftwareFault Event shall be generated when a software fault takes place on the Node. + */ + public class SoftwareFault { + /** + * The ID field shall be set to the ID of the software thread in which the last software fault occurred. + */ + public BigInteger id; // uint64 + /** + * The Name field shall be set to a manufacturer-specified name or prefix of the software thread in which the + * last software fault occurred. + */ + public String name; // string + /** + * The FaultRecording field shall be a manufacturer-specified payload intended to convey information to assist + * in further diagnosing or debugging a software fault. The FaultRecording field may be used to convey + * information such as, but not limited to, thread backtraces or register contents. + */ + public OctetString faultRecording; // octstr + + public SoftwareFault(BigInteger id, String name, OctetString faultRecording) { + this.id = id; + this.name = name; + this.faultRecording = faultRecording; + } + } + + public class ThreadMetricsStruct { + /** + * The Id field shall be a server-assigned per-thread unique ID that is constant for the duration of the thread. + * Efforts SHOULD be made to avoid reusing ID values when possible. + */ + public BigInteger id; // uint64 + /** + * The Name field shall be set to a vendor defined name or prefix of the software thread that is static for the + * duration of the thread. + */ + public String name; // string + /** + * The StackFreeCurrent field shall indicate the current amount of stack memory, in bytes, that are not being + * utilized on the respective thread. + */ + public Integer stackFreeCurrent; // uint32 + /** + * The StackFreeMinimum field shall indicate the minimum amount of stack memory, in bytes, that has been + * available at any point between the current time and this attribute being reset or initialized on the + * respective thread. This value shall only be reset upon a Node reboot or upon receiving of the ResetWatermarks + * command. + */ + public Integer stackFreeMinimum; // uint32 + /** + * The StackSize field shall indicate the amount of stack memory, in bytes, that has been allocated for use by + * the respective thread. + */ + public Integer stackSize; // uint32 + + public ThreadMetricsStruct(BigInteger id, String name, Integer stackFreeCurrent, Integer stackFreeMinimum, + Integer stackSize) { + this.id = id; + this.name = name; + this.stackFreeCurrent = stackFreeCurrent; + this.stackFreeMinimum = stackFreeMinimum; + this.stackSize = stackSize; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Node makes available the metrics for high watermark related to memory consumption. + */ + public boolean watermarks; + + public FeatureMap(boolean watermarks) { + this.watermarks = watermarks; + } + } + + public SoftwareDiagnosticsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 52, "SoftwareDiagnostics"); + } + + protected SoftwareDiagnosticsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Receipt of this command shall reset the following values which track high and lower watermarks: + * • The StackFreeMinimum field of the ThreadMetrics attribute + * • The CurrentHeapHighWatermark attribute This command has no payload. + * ### Effect on Receipt + * On receipt of this command, the Node shall make the following modifications to attributes it supports: + * If implemented, the server shall set the value of the CurrentHeapHighWatermark attribute to the value of the + * CurrentHeapUsed attribute. + * If implemented, the server shall set the value of the StackFreeMinimum field for every thread to the value of the + * corresponding thread’s StackFreeCurrent field. + */ + public static ClusterCommand resetWatermarks() { + return new ClusterCommand("resetWatermarks"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "threadMetrics : " + threadMetrics + "\n"; + str += "currentHeapFree : " + currentHeapFree + "\n"; + str += "currentHeapUsed : " + currentHeapUsed + "\n"; + str += "currentHeapHighWatermark : " + currentHeapHighWatermark + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SwitchCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SwitchCluster.java new file mode 100644 index 00000000000..4b129466444 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/SwitchCluster.java @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * Switch + * + * @author Dan Cunningham - Initial contribution + */ +public class SwitchCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x003B; + public static final String CLUSTER_NAME = "Switch"; + public static final String CLUSTER_PREFIX = "switch"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_NUMBER_OF_POSITIONS = "numberOfPositions"; + public static final String ATTRIBUTE_CURRENT_POSITION = "currentPosition"; + public static final String ATTRIBUTE_MULTI_PRESS_MAX = "multiPressMax"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the maximum number of positions the switch has. Any kind of switch has a minimum of 2 positions. Also + * see Multi Position Details for the case NumberOfPositions>2. + */ + public Integer numberOfPositions; // 0 uint8 R V + /** + * Indicates the position of the switch. The valid range is zero to NumberOfPositions - 1. + * CurrentPosition value 0 shall be assigned to the default position of the switch: for example the "open" + * state of a rocker switch, or the "idle" state of a push button switch. + */ + public Integer currentPosition; // 1 uint8 R V + /** + * Indicates how many consecutive presses can be detected and reported by a momentary switch which supports + * multi-press (MSM feature flag set). + * For example, a momentary switch supporting single press, double press and triple press, but not quad press and + * beyond, would return the value 3. + * When more than MultiPressMax presses are detected within a multi-press sequence: + * • The server for cluster revision < 2 SHOULD generate a MultiPressComplete event with the + * TotalNumberOfPressesCounted field set to the value of the MultiPressMax attribute, and avoid generating any + * further InitialPress and MultiPressOngoing events until the switch has become fully idle (i.e. no longer in the + * process of counting presses within the multipress). + * • The server for cluster revision >= 2 shall generate a MultiPressComplete event with the + * TotalNumberOfPressesCounted field set to zero (indicating an aborted sequence), and shall NOT generate any + * further InitialPress and MultiPressOngoing events until the switch has become fully idle (i.e. no longer in the + * process of counting presses within the multipress). + * This approach avoids unintentionally causing intermediate actions where there is a very long sequence of presses + * beyond MultiPressMax that may be taken in account specially by switches (e.g. to trigger special behavior such as + * factory reset for which generating events towards the client is not appropriate). + */ + public Integer multiPressMax; // 2 uint8 R V + // Structs + + /** + * This event shall be generated, when the latching switch is moved to a new position. It may have been delayed by + * debouncing within the switch. + */ + public class SwitchLatched { + /** + * This field shall indicate the new value of the CurrentPosition attribute, i.e. after the move. + */ + public Integer newPosition; // uint8 + + public SwitchLatched(Integer newPosition) { + this.newPosition = newPosition; + } + } + + /** + * This event shall be generated, when the momentary switch starts to be pressed (after debouncing). + */ + public class InitialPress { + /** + * This field shall indicate the new value of the CurrentPosition attribute, i.e. while pressed. + */ + public Integer newPosition; // uint8 + + public InitialPress(Integer newPosition) { + this.newPosition = newPosition; + } + } + + /** + * This event shall be generated when the momentary switch has been pressed for a "long" time. The time + * interval constituting a "long" time is manufacturer-determined, since it depends on the switch physics. + * • When the AS feature flag is set, this event: + * ◦ shall NOT be generated during a multi-press sequence (since a long press is a separate cycle from any + * multi-press cycles); + * ◦ shall only be generated after the first InitialPress following a MultiPressComplete when a long press is + * detected after the idle time. + * • Else, when the MSM feature flag is set, this event: + * ◦ shall NOT be generated during a multi-press sequence (since a long press is a separate cycle from any + * multi-press cycles); + * ◦ shall only be generated after the first InitialPress following a MultiPressComplete when a long press is + * detected after the idle time; + * ◦ shall NOT be generated after a MultiPressOngoing event without an intervening MultiPressComplete event. + * The above constraints imply that for a given activity detection cycle of a switch having MSM and/or MSL feature + * flags set, the entire activity is either a single long press detection cycle of (InitialPress, LongPress, + * LongRelease), or a single multi-press detection cycle (ending in MultiPressComplete), where presses that would + * otherwise be reported as long presses are instead reported as a counted press in the MultiPressComplete event, + * and as InitialPress/ShortRelease pairs otherwise (where applicable). + * The rationale for this constraint is the ambiguity of interpretation of events when mixing long presses and + * multi-press events. + */ + public class LongPress { + /** + * This field shall indicate the new value of the CurrentPosition attribute, i.e. while pressed. + */ + public Integer newPosition; // uint8 + + public LongPress(Integer newPosition) { + this.newPosition = newPosition; + } + } + + /** + * If the server has the Action Switch (AS) feature flag set, this event shall NOT be generated at all, since + * setting the Action Switch feature flag forbids the Momentary Switch ShortRelease (MSR) feature flag from being + * set. Otherwise, the following paragraphs describe the situations where this event is generated. + * This event shall be generated, when the momentary switch has been released (after debouncing). + * • If the server has the Momentary Switch LongPress (MSL) feature flag set, then this event shall be generated + * when the switch is released if no LongPress event had been generated since the previous InitialPress event. + * • If the server does not have the Momentary Switch LongPress (MSL) feature flag set, this event shall be + * generated when the switch is released - even when the switch was pressed for a long time. + * • Also see Section 1.13.7, “Sequence of generated events”. + */ + public class ShortRelease { + /** + * This field shall indicate the previous value of the CurrentPosition attribute, i.e. just prior to release. + */ + public Integer previousPosition; // uint8 + + public ShortRelease(Integer previousPosition) { + this.previousPosition = previousPosition; + } + } + + /** + * This event shall be generated, when the momentary switch has been released (after debouncing) and after having + * been pressed for a long time, i.e. this event shall be generated when the switch is released if a LongPress event + * has been generated since the previous InitialPress event. Also see Section 1.13.7, “Sequence of generated + * events”. + */ + public class LongRelease { + /** + * This field shall indicate the previous value of the CurrentPosition attribute, i.e. just prior to release. + */ + public Integer previousPosition; // uint8 + + public LongRelease(Integer previousPosition) { + this.previousPosition = previousPosition; + } + } + + /** + * If the server has the Action Switch (AS) feature flag set, this event shall NOT be generated at all. Otherwise, + * the following paragraphs describe the situations where this event is generated. + * This event shall be generated to indicate how many times the momentary switch has been pressed in a multi-press + * sequence, during that sequence. See Multi Press Details below. + */ + public class MultiPressOngoing { + /** + * This field shall indicate the new value of the CurrentPosition attribute, i.e. while pressed. + */ + public Integer newPosition; // uint8 + /** + * This field shall contain: + * • a value of 2 when the second press of a multi-press sequence has been detected, + * • a value of 3 when the third press of a multi-press sequence has been detected, + * • a value of N when the Nth press of a multi-press sequence has been detected. + */ + public Integer currentNumberOfPressesCounted; // uint8 + + public MultiPressOngoing(Integer newPosition, Integer currentNumberOfPressesCounted) { + this.newPosition = newPosition; + this.currentNumberOfPressesCounted = currentNumberOfPressesCounted; + } + } + + /** + * This event shall be generated to indicate how many times the momentary switch has been pressed in a multi-press + * sequence, after it has been detected that the sequence has ended. See Multi Press Details. + * The PreviousPosition field shall indicate the previous value of the CurrentPosition attribute, i.e. just prior to + * release. + * The TotalNumberOfPressesCounted field shall contain: + * • a value of 0 when there was an aborted multi-press sequence, where the number of presses goes beyond + * MultiPressMax presses, + * • a value of 1 when there was exactly one press in a multi-press sequence (and the sequence has ended), i.e. + * there was no double press (or more), + * • a value of 2 when there were exactly two presses in a multi-press sequence (and the sequence has ended), + * • a value of 3 when there were exactly three presses in a multi-press sequence (and the sequence has ended), + * • a value of N when there were exactly N presses in a multi-press sequence (and the sequence has ended). + * > [!NOTE] + * > The introduction of TotalNumberOfPressesCounted supporting the value 0 may impact clients of switches using + * cluster revision 1 since such servers would not use this value of TotalNumberOfPressesCounted to indicate an + * aborted sequence. Clients SHOULD always act using the TotalNumberOfPressesCounted field taken into account since + * for values from 1 to MultiPressMax, the user action that led to the event was different depending on the count. + */ + public class MultiPressComplete { + public Integer previousPosition; // uint8 + public Integer totalNumberOfPressesCounted; // uint8 + + public MultiPressComplete(Integer previousPosition, Integer totalNumberOfPressesCounted) { + this.previousPosition = previousPosition; + this.totalNumberOfPressesCounted = totalNumberOfPressesCounted; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature flag is for a switch that maintains its position after being pressed (or turned). + */ + public boolean latchingSwitch; + /** + * + * This feature flag is for a switch that does not maintain its position after being pressed (or turned). After + * releasing, it goes back to its idle position. + */ + public boolean momentarySwitch; + /** + * + * This feature flag is for a momentary switch that can distinguish and report release events. + */ + public boolean momentarySwitchRelease; + /** + * + * This feature flag is for a momentary switch that can distinguish and report long presses from short presses. + */ + public boolean momentarySwitchLongPress; + /** + * + * This feature flag is for a momentary switch that can distinguish and report double press and potentially + * multiple presses with more events, such as triple press, etc. + */ + public boolean momentarySwitchMultiPress; + /** + * + * This feature flag indicates simplified handling of events for multi-press-capable switches. See Multi Press + * Details. + */ + public boolean actionSwitch; + + public FeatureMap(boolean latchingSwitch, boolean momentarySwitch, boolean momentarySwitchRelease, + boolean momentarySwitchLongPress, boolean momentarySwitchMultiPress, boolean actionSwitch) { + this.latchingSwitch = latchingSwitch; + this.momentarySwitch = momentarySwitch; + this.momentarySwitchRelease = momentarySwitchRelease; + this.momentarySwitchLongPress = momentarySwitchLongPress; + this.momentarySwitchMultiPress = momentarySwitchMultiPress; + this.actionSwitch = actionSwitch; + } + } + + public SwitchCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 59, "Switch"); + } + + protected SwitchCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "numberOfPositions : " + numberOfPositions + "\n"; + str += "currentPosition : " + currentPosition + "\n"; + str += "multiPressMax : " + multiPressMax + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TargetNavigatorCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TargetNavigatorCluster.java new file mode 100644 index 00000000000..c0e7e207bb5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TargetNavigatorCluster.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * TargetNavigator + * + * @author Dan Cunningham - Initial contribution + */ +public class TargetNavigatorCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0505; + public static final String CLUSTER_NAME = "TargetNavigator"; + public static final String CLUSTER_PREFIX = "targetNavigator"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_TARGET_LIST = "targetList"; + public static final String ATTRIBUTE_CURRENT_TARGET = "currentTarget"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates a list of targets that can be navigated to within the experience presented to the user by the Endpoint + * (Video Player or Content App). The list shall NOT contain any entries with the same Identifier in the + * TargetInfoStruct object. + */ + public List targetList; // 0 list R V + /** + * Indicates the Identifier for the target which is currently in foreground on the corresponding Endpoint (Video + * Player or Content App), or 0xFF to indicate that no target is in the foreground. + * When not 0xFF, the CurrentTarget shall be an Identifier value contained within one of the TargetInfoStruct + * objects in the TargetList attribute. + */ + public Integer currentTarget; // 1 uint8 R V + // Structs + + /** + * This event shall be generated when there is a change in either the active target or the list of available targets + * or both. + */ + public class TargetUpdated { + public List targetList; // list + public Integer currentTarget; // uint8 + public OctetString data; // octstr + + public TargetUpdated(List targetList, Integer currentTarget, OctetString data) { + this.targetList = targetList; + this.currentTarget = currentTarget; + this.data = data; + } + } + + /** + * This indicates an object describing the navigable target. + */ + public class TargetInfoStruct { + /** + * This field shall contain an unique id within the TargetList. + */ + public Integer identifier; // uint8 + /** + * This field shall contain a name string for the TargetInfoStruct. + */ + public String name; // string + + public TargetInfoStruct(Integer identifier, String name) { + this.identifier = identifier; + this.name = name; + } + } + + // Enums + public enum StatusEnum implements MatterEnum { + SUCCESS(0, "Success"), + TARGET_NOT_FOUND(1, "Target Not Found"), + NOT_ALLOWED(2, "Not Allowed"); + + public final Integer value; + public final String label; + + private StatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public TargetNavigatorCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1285, "TargetNavigator"); + } + + protected TargetNavigatorCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt, this shall navigation the UX to the target identified. + */ + public static ClusterCommand navigateTarget(Integer target, String data) { + Map map = new LinkedHashMap<>(); + if (target != null) { + map.put("target", target); + } + if (data != null) { + map.put("data", data); + } + return new ClusterCommand("navigateTarget", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "targetList : " + targetList + "\n"; + str += "currentTarget : " + currentTarget + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TemperatureControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TemperatureControlCluster.java new file mode 100644 index 00000000000..935f06c6722 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TemperatureControlCluster.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * TemperatureControl + * + * @author Dan Cunningham - Initial contribution + */ +public class TemperatureControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0056; + public static final String CLUSTER_NAME = "TemperatureControl"; + public static final String CLUSTER_PREFIX = "temperatureControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_TEMPERATURE_SETPOINT = "temperatureSetpoint"; + public static final String ATTRIBUTE_MIN_TEMPERATURE = "minTemperature"; + public static final String ATTRIBUTE_MAX_TEMPERATURE = "maxTemperature"; + public static final String ATTRIBUTE_STEP = "step"; + public static final String ATTRIBUTE_SELECTED_TEMPERATURE_LEVEL = "selectedTemperatureLevel"; + public static final String ATTRIBUTE_SUPPORTED_TEMPERATURE_LEVELS = "supportedTemperatureLevels"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the desired Temperature Setpoint on the device. + */ + public Integer temperatureSetpoint; // 0 temperature R V + /** + * Indicates the minimum temperature to which the TemperatureSetpoint attribute may be set. + */ + public Integer minTemperature; // 1 temperature R V + /** + * Indicates the maximum temperature to which the TemperatureSetpoint attribute may be set. + * If the Step attribute is supported, this attribute shall be such that MaxTemperature = MinTemperature + Step + * * n, where n is an integer and n > 0. If the Step attribute is not supported, this attribute shall be such + * that MaxTemperature > MinTemperature. + */ + public Integer maxTemperature; // 2 temperature R V + /** + * Indicates the discrete value by which the TemperatureSetpoint attribute can be changed via the SetTemperature + * command. + * For example, if the value of MinTemperature is 25.00C (2500) and the Step value is 0.50C (50), valid values of + * the TargetTemperature field of the SetTemperature command would be 25.50C (2550), 26.00C (2600), 26.50C (2650), + * etc. + */ + public Integer step; // 3 temperature R V + /** + * Indicates the currently selected temperature level setting of the server. This attribute shall be the positional + * index of the list item in the SupportedTemperatureLevels list that represents the currently selected temperature + * level setting of the server. + */ + public Integer selectedTemperatureLevel; // 4 uint8 R V + /** + * Indicates the list of supported temperature level settings that may be selected via the TargetTemperatureLevel + * field in the SetTemperature command. Each string is readable text that describes each temperature level setting + * in a way that can be easily understood by humans. For example, a washing machine can have temperature levels like + * "Cold", "Warm", and "Hot". Each string is specified by the manufacturer. + * Each item in this list shall represent a unique temperature level. Each entry in this list shall have a unique + * value. The entries in this list shall appear in order of increasing temperature level with list item 0 being the + * setting with the lowest temperature level. + */ + public List supportedTemperatureLevels; // 5 list R V + + // Bitmaps + public static class FeatureMap { + /** + * + * For devices that use an actual temperature value for the temperature setpoint, such as some water heaters, + * the feature TN shall be used. Note that this cluster provides and supports temperatures in degrees Celsius + * via the temperature data type. + */ + public boolean temperatureNumber; + /** + * + * For devices that use vendor-specific temperature levels for the temperature setpoint, such as some washers, + * the feature TL shall be used. + */ + public boolean temperatureLevel; + /** + * + * For devices that support discrete temperature setpoints that are larger than the temperature resolution + * imposed via the temperature data type, the Step feature may be used. + */ + public boolean temperatureStep; + + public FeatureMap(boolean temperatureNumber, boolean temperatureLevel, boolean temperatureStep) { + this.temperatureNumber = temperatureNumber; + this.temperatureLevel = temperatureLevel; + this.temperatureStep = temperatureStep; + } + } + + public TemperatureControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 86, "TemperatureControl"); + } + + protected TemperatureControlCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand setTemperature(Integer targetTemperature, Integer targetTemperatureLevel) { + Map map = new LinkedHashMap<>(); + if (targetTemperature != null) { + map.put("targetTemperature", targetTemperature); + } + if (targetTemperatureLevel != null) { + map.put("targetTemperatureLevel", targetTemperatureLevel); + } + return new ClusterCommand("setTemperature", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "temperatureSetpoint : " + temperatureSetpoint + "\n"; + str += "minTemperature : " + minTemperature + "\n"; + str += "maxTemperature : " + maxTemperature + "\n"; + str += "step : " + step + "\n"; + str += "selectedTemperatureLevel : " + selectedTemperatureLevel + "\n"; + str += "supportedTemperatureLevels : " + supportedTemperatureLevels + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TemperatureMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TemperatureMeasurementCluster.java new file mode 100644 index 00000000000..05d19fd41c5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TemperatureMeasurementCluster.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * TemperatureMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class TemperatureMeasurementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0402; + public static final String CLUSTER_NAME = "TemperatureMeasurement"; + public static final String CLUSTER_PREFIX = "temperatureMeasurement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_MEASURED_VALUE = "measuredValue"; + public static final String ATTRIBUTE_MIN_MEASURED_VALUE = "minMeasuredValue"; + public static final String ATTRIBUTE_MAX_MEASURED_VALUE = "maxMeasuredValue"; + public static final String ATTRIBUTE_TOLERANCE = "tolerance"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the measured temperature. The null value indicates that the temperature is unknown. + */ + public Integer measuredValue; // 0 temperature R V + /** + * Indicates the minimum value of MeasuredValue that is capable of being measured. See Measured Value for more + * details. + * The null value indicates that the value is not available. + */ + public Integer minMeasuredValue; // 1 temperature R V + /** + * This attribute indicates the maximum value of MeasuredValue that is capable of being measured. See Measured Value + * for more details. + * The null value indicates that the value is not available. + */ + public Integer maxMeasuredValue; // 2 temperature R V + /** + * See Measured Value. + */ + public Integer tolerance; // 3 uint16 R V + + public TemperatureMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1026, "TemperatureMeasurement"); + } + + protected TemperatureMeasurementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "measuredValue : " + measuredValue + "\n"; + str += "minMeasuredValue : " + minMeasuredValue + "\n"; + str += "maxMeasuredValue : " + maxMeasuredValue + "\n"; + str += "tolerance : " + tolerance + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThermostatCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThermostatCluster.java new file mode 100644 index 00000000000..3ae0fc53866 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThermostatCluster.java @@ -0,0 +1,1667 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * Thermostat + * + * @author Dan Cunningham - Initial contribution + */ +public class ThermostatCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0201; + public static final String CLUSTER_NAME = "Thermostat"; + public static final String CLUSTER_PREFIX = "thermostat"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_LOCAL_TEMPERATURE = "localTemperature"; + public static final String ATTRIBUTE_OUTDOOR_TEMPERATURE = "outdoorTemperature"; + public static final String ATTRIBUTE_OCCUPANCY = "occupancy"; + public static final String ATTRIBUTE_ABS_MIN_HEAT_SETPOINT_LIMIT = "absMinHeatSetpointLimit"; + public static final String ATTRIBUTE_ABS_MAX_HEAT_SETPOINT_LIMIT = "absMaxHeatSetpointLimit"; + public static final String ATTRIBUTE_ABS_MIN_COOL_SETPOINT_LIMIT = "absMinCoolSetpointLimit"; + public static final String ATTRIBUTE_ABS_MAX_COOL_SETPOINT_LIMIT = "absMaxCoolSetpointLimit"; + public static final String ATTRIBUTE_PI_COOLING_DEMAND = "piCoolingDemand"; + public static final String ATTRIBUTE_PI_HEATING_DEMAND = "piHeatingDemand"; + public static final String ATTRIBUTE_LOCAL_TEMPERATURE_CALIBRATION = "localTemperatureCalibration"; + public static final String ATTRIBUTE_OCCUPIED_COOLING_SETPOINT = "occupiedCoolingSetpoint"; + public static final String ATTRIBUTE_OCCUPIED_HEATING_SETPOINT = "occupiedHeatingSetpoint"; + public static final String ATTRIBUTE_UNOCCUPIED_COOLING_SETPOINT = "unoccupiedCoolingSetpoint"; + public static final String ATTRIBUTE_UNOCCUPIED_HEATING_SETPOINT = "unoccupiedHeatingSetpoint"; + public static final String ATTRIBUTE_MIN_HEAT_SETPOINT_LIMIT = "minHeatSetpointLimit"; + public static final String ATTRIBUTE_MAX_HEAT_SETPOINT_LIMIT = "maxHeatSetpointLimit"; + public static final String ATTRIBUTE_MIN_COOL_SETPOINT_LIMIT = "minCoolSetpointLimit"; + public static final String ATTRIBUTE_MAX_COOL_SETPOINT_LIMIT = "maxCoolSetpointLimit"; + public static final String ATTRIBUTE_MIN_SETPOINT_DEAD_BAND = "minSetpointDeadBand"; + public static final String ATTRIBUTE_REMOTE_SENSING = "remoteSensing"; + public static final String ATTRIBUTE_CONTROL_SEQUENCE_OF_OPERATION = "controlSequenceOfOperation"; + public static final String ATTRIBUTE_SYSTEM_MODE = "systemMode"; + public static final String ATTRIBUTE_THERMOSTAT_RUNNING_MODE = "thermostatRunningMode"; + public static final String ATTRIBUTE_START_OF_WEEK = "startOfWeek"; + public static final String ATTRIBUTE_NUMBER_OF_WEEKLY_TRANSITIONS = "numberOfWeeklyTransitions"; + public static final String ATTRIBUTE_NUMBER_OF_DAILY_TRANSITIONS = "numberOfDailyTransitions"; + public static final String ATTRIBUTE_TEMPERATURE_SETPOINT_HOLD = "temperatureSetpointHold"; + public static final String ATTRIBUTE_TEMPERATURE_SETPOINT_HOLD_DURATION = "temperatureSetpointHoldDuration"; + public static final String ATTRIBUTE_THERMOSTAT_PROGRAMMING_OPERATION_MODE = "thermostatProgrammingOperationMode"; + public static final String ATTRIBUTE_THERMOSTAT_RUNNING_STATE = "thermostatRunningState"; + public static final String ATTRIBUTE_SETPOINT_CHANGE_SOURCE = "setpointChangeSource"; + public static final String ATTRIBUTE_SETPOINT_CHANGE_AMOUNT = "setpointChangeAmount"; + public static final String ATTRIBUTE_SETPOINT_CHANGE_SOURCE_TIMESTAMP = "setpointChangeSourceTimestamp"; + public static final String ATTRIBUTE_OCCUPIED_SETBACK = "occupiedSetback"; + public static final String ATTRIBUTE_OCCUPIED_SETBACK_MIN = "occupiedSetbackMin"; + public static final String ATTRIBUTE_OCCUPIED_SETBACK_MAX = "occupiedSetbackMax"; + public static final String ATTRIBUTE_UNOCCUPIED_SETBACK = "unoccupiedSetback"; + public static final String ATTRIBUTE_UNOCCUPIED_SETBACK_MIN = "unoccupiedSetbackMin"; + public static final String ATTRIBUTE_UNOCCUPIED_SETBACK_MAX = "unoccupiedSetbackMax"; + public static final String ATTRIBUTE_EMERGENCY_HEAT_DELTA = "emergencyHeatDelta"; + public static final String ATTRIBUTE_AC_TYPE = "acType"; + public static final String ATTRIBUTE_AC_CAPACITY = "acCapacity"; + public static final String ATTRIBUTE_AC_REFRIGERANT_TYPE = "acRefrigerantType"; + public static final String ATTRIBUTE_AC_COMPRESSOR_TYPE = "acCompressorType"; + public static final String ATTRIBUTE_AC_ERROR_CODE = "acErrorCode"; + public static final String ATTRIBUTE_AC_LOUVER_POSITION = "acLouverPosition"; + public static final String ATTRIBUTE_AC_COIL_TEMPERATURE = "acCoilTemperature"; + public static final String ATTRIBUTE_AC_CAPACITY_FORMAT = "acCapacityFormat"; + public static final String ATTRIBUTE_PRESET_TYPES = "presetTypes"; + public static final String ATTRIBUTE_SCHEDULE_TYPES = "scheduleTypes"; + public static final String ATTRIBUTE_NUMBER_OF_PRESETS = "numberOfPresets"; + public static final String ATTRIBUTE_NUMBER_OF_SCHEDULES = "numberOfSchedules"; + public static final String ATTRIBUTE_NUMBER_OF_SCHEDULE_TRANSITIONS = "numberOfScheduleTransitions"; + public static final String ATTRIBUTE_NUMBER_OF_SCHEDULE_TRANSITION_PER_DAY = "numberOfScheduleTransitionPerDay"; + public static final String ATTRIBUTE_ACTIVE_PRESET_HANDLE = "activePresetHandle"; + public static final String ATTRIBUTE_ACTIVE_SCHEDULE_HANDLE = "activeScheduleHandle"; + public static final String ATTRIBUTE_PRESETS = "presets"; + public static final String ATTRIBUTE_SCHEDULES = "schedules"; + public static final String ATTRIBUTE_SETPOINT_HOLD_EXPIRY_TIMESTAMP = "setpointHoldExpiryTimestamp"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the current Calculated Local Temperature, when available. + * • If the LTNE feature is not supported: + * ◦ If the LocalTemperatureCalibration is invalid or currently unavailable, the attribute shall report null. + * ◦ If the LocalTemperatureCalibration is valid, the attribute shall report that value. + * • Otherwise, if the LTNE feature is supported, there is no feedback externally available for the + * LocalTemperatureCalibration. In that case, the LocalTemperature attribute shall always report null. + */ + public Integer localTemperature; // 0 temperature R V + /** + * Indicates the outdoor temperature, as measured locally or remotely (over the network). + */ + public Integer outdoorTemperature; // 1 temperature R V + /** + * Indicates whether the heated/cooled space is occupied or not, as measured locally or remotely (over the network). + */ + public OccupancyBitmap occupancy; // 2 OccupancyBitmap R V + /** + * Indicates the absolute minimum level that the heating setpoint may be set to. This is a limitation imposed by the + * manufacturer. + * ### Refer to Setpoint Limits for constraints + */ + public Integer absMinHeatSetpointLimit; // 3 temperature R V + public Integer absMaxHeatSetpointLimit; // 4 temperature R V + public Integer absMinCoolSetpointLimit; // 5 temperature R V + /** + * Indicates the absolute maximum level that the cooling setpoint may be set to. This is a limitation imposed by the + * manufacturer. + * ### Refer to Setpoint Limits for constraints + */ + public Integer absMaxCoolSetpointLimit; // 6 temperature R V + /** + * Indicates the level of cooling demanded by the PI (proportional integral) control loop in use by the thermostat + * (if any), in percent. This value is 0 when the thermostat is in “off” or “heating” mode. + * This attribute is reported regularly and may be used to control a cooling device. + */ + public Integer piCoolingDemand; // 7 uint8 R V + /** + * Indicates the level of heating demanded by the PI loop in percent. This value is 0 when the thermostat is in + * “off” or “cooling” mode. + * This attribute is reported regularly and may be used to control a heating device. + */ + public Integer piHeatingDemand; // 8 uint8 R V + /** + * Indicates the offset the Thermostat server shall make to the measured temperature (locally or remotely) to adjust + * the Calculated Local Temperature prior to using, displaying or reporting it. + * The purpose of this attribute is to adjust the calibration of the Thermostat server per the user’s preferences + * (e.g., to match if there are multiple servers displaying different values for the same HVAC area) or compensate + * for variability amongst temperature sensors. + * If a Thermostat client attempts to write LocalTemperatureCalibration attribute to an unsupported value (e.g., out + * of the range supported by the Thermostat server), the Thermostat server shall respond with a status of SUCCESS + * and set the value of LocalTemperatureCalibration to the upper or lower limit reached. + * > [!NOTE] + * > Prior to revision 8 of this cluster specification the value of this attribute was constrained to a range of + * -2.5°C to 2.5°C. + */ + public Integer localTemperatureCalibration; // 16 SignedTemperature RW VM + /** + * Indicates the cooling mode setpoint when the room is occupied. Refer to Setpoint Limits for constraints. + * If an attempt is made to set this attribute to a value greater than MaxCoolSetpointLimit or less than + * MinCoolSetpointLimit, a response with the status code CONSTRAINT_ERROR shall be returned. + * If this attribute is set to a value that is less than (OccupiedHeatingSetpoint + MinSetpointDeadBand), the value + * of OccupiedHeatingSetpoint shall be adjusted to (OccupiedCoolingSetpoint - MinSetpointDeadBand). + * If the occupancy status of the room is unknown, this attribute shall be used as the cooling mode setpoint. + * If a client changes the value of this attribute, the server supports the PRES feature, and the server either does + * not support the OCC feature or the Occupied bit is set on the Occupancy attribute, the value of the + * ActivePresetHandle attribute shall be set to null. + */ + public Integer occupiedCoolingSetpoint; // 17 temperature RW VO + /** + * Indicates the heating mode setpoint when the room is occupied. Refer to Setpoint Limits for constraints. + * If an attempt is made to set this attribute to a value greater than MaxHeatSetpointLimit or less than + * MinHeatSetpointLimit, a response with the status code CONSTRAINT_ERROR shall be returned. + * If this attribute is set to a value that is greater than (OccupiedCoolingSetpoint - MinSetpointDeadBand), the + * value of OccupiedCoolingSetpoint shall be adjusted to (OccupiedHeatingSetpoint + MinSetpointDeadBand). + * If the occupancy status of the room is unknown, this attribute shall be used as the heating mode setpoint. + * If a client changes the value of this attribute, the server supports the PRES feature, and the server either does + * not support the OCC feature or the Occupied bit is set on the Occupancy attribute, the value of the + * ActivePresetHandle attribute shall be set to null. + */ + public Integer occupiedHeatingSetpoint; // 18 temperature RW VO + /** + * Indicates the cooling mode setpoint when the room is unoccupied. Refer to Setpoint Limits for constraints. + * If an attempt is made to set this attribute to a value greater than MaxCoolSetpointLimit or less than + * MinCoolSetpointLimit, a response with the status code CONSTRAINT_ERROR shall be returned. + * If this attribute is set to a value that is less than (UnoccupiedHeatingSetpoint + MinSetpointDeadBand), the + * value of UnoccupiedHeatingSetpoint shall be adjusted to (UnoccupiedCoolingSetpoint - MinSetpointDeadBand). + * If the occupancy status of the room is unknown, this attribute shall NOT be used. + * If a client changes the value of this attribute, the server supports the PRES and OCC features, and the Occupied + * bit is not set on the Occupancy attribute, the value of the ActivePresetHandle attribute shall be set to null. + */ + public Integer unoccupiedCoolingSetpoint; // 19 temperature RW VO + /** + * Indicates the heating mode setpoint when the room is unoccupied. Refer to Setpoint Limits for constraints. + * If an attempt is made to set this attribute to a value greater than MaxHeatSetpointLimit or less than + * MinHeatSetpointLimit, a response with the status code CONSTRAINT_ERROR shall be returned. + * If this attribute is set to a value that is greater than (UnoccupiedCoolingSetpoint - MinSetpointDeadBand), the + * value of UnoccupiedCoolingSetpoint shall be adjusted to (UnoccupiedHeatingSetpoint + MinSetpointDeadBand). + * If the occupancy status of the room is unknown, this attribute shall NOT be used. + * If a client changes the value of this attribute, the server supports the PRES and OCC features, and the Occupied + * bit is not set on the Occupancy attribute, the value of the ActivePresetHandle attribute shall be set to null. + */ + public Integer unoccupiedHeatingSetpoint; // 20 temperature RW VO + /** + * Indicates the minimum level that the heating setpoint may be set to. + * This attribute, and the following three attributes, allow the user to define setpoint limits more constrictive + * than the manufacturer imposed ones. Limiting users (e.g., in a commercial building) to such setpoint limits can + * help conserve power. + * Refer to Setpoint Limits for constraints. If an attempt is made to set this attribute to a value which conflicts + * with setpoint values then those setpoints shall be adjusted by the minimum amount to permit this attribute to be + * set to the desired value. If an attempt is made to set this attribute to a value which is not consistent with the + * constraints and cannot be resolved by modifying setpoints then a response with the status code CONSTRAINT_ERROR + * shall be returned. + */ + public Integer minHeatSetpointLimit; // 21 temperature RW VM + /** + * Indicates the maximum level that the heating setpoint may be set to. + * Refer to Setpoint Limits for constraints. If an attempt is made to set this attribute to a value which conflicts + * with setpoint values then those setpoints shall be adjusted by the minimum amount to permit this attribute to be + * set to the desired value. If an attempt is made to set this attribute to a value which is not consistent with the + * constraints and cannot be resolved by modifying setpoints then a response with the status code CONSTRAINT_ERROR + * shall be returned. + */ + public Integer maxHeatSetpointLimit; // 22 temperature RW VM + /** + * Indicates the minimum level that the cooling setpoint may be set to. + * Refer to Setpoint Limits for constraints. If an attempt is made to set this attribute to a value which conflicts + * with setpoint values then those setpoints shall be adjusted by the minimum amount to permit this attribute to be + * set to the desired value. If an attempt is made to set this attribute to a value which is not consistent with the + * constraints and cannot be resolved by modifying setpoints then a response with the status code CONSTRAINT_ERROR + * shall be returned. + */ + public Integer minCoolSetpointLimit; // 23 temperature RW VM + /** + * Indicates the maximum level that the cooling setpoint may be set to. + * Refer to Setpoint Limits for constraints. If an attempt is made to set this attribute to a value which conflicts + * with setpoint values then those setpoints shall be adjusted by the minimum amount to permit this attribute to be + * set to the desired value. If an attempt is made to set this attribute to a value which is not consistent with the + * constraints and cannot be resolved by modifying setpoints then a response with the status code CONSTRAINT_ERROR + * shall be returned. + */ + public Integer maxCoolSetpointLimit; // 24 temperature RW VM + /** + * On devices which support the AUTO feature, this attribute shall indicate the minimum difference between the Heat + * Setpoint and the Cool Setpoint. + * Refer to Setpoint Limits for constraints. + * > [!NOTE] + * > Prior to revision 8 of this cluster specification the value of this attribute was constrained to a range of + * 0°C to 2.5°C. + * For backwards compatibility, this attribute is optionally writeable. However any writes to this attribute shall + * be silently ignored. + */ + public Integer minSetpointDeadBand; // 25 SignedTemperature R[W] VM + /** + * Indicates when the local temperature, outdoor temperature and occupancy are being sensed by remote networked + * sensors, rather than internal sensors. + * If the LTNE feature is present in the server, the LocalTemperature RemoteSensing bit value shall always report a + * value of 0. + * If the LocalTemperature RemoteSensing bit is written with a value of 1 when the LTNE feature is present, the + * write shall fail and the server shall report a CONSTRAINT_ERROR. + */ + public RemoteSensingBitmap remoteSensing; // 26 RemoteSensingBitmap RW VM + /** + * Indicates the overall operating environment of the thermostat, and thus the possible system modes that the + * thermostat can operate in. + * If an attempt is made to write to this attribute, the server shall silently ignore the write and the value of + * this attribute shall remain unchanged. This behavior is in place for backwards compatibility with existing + * thermostats. + */ + public ControlSequenceOfOperationEnum controlSequenceOfOperation; // 27 ControlSequenceOfOperationEnum RW VM + /** + * Indicates the current operating mode of the thermostat. Its value shall be limited by the + * ControlSequenceOfOperation attribute. + */ + public SystemModeEnum systemMode; // 28 SystemModeEnum RW VM + /** + * Indicates the running mode of the thermostat. This attribute uses the same values as SystemModeEnum but can only + * be Off, Cool or Heat. This attribute is intended to provide additional information when the thermostat’s system + * mode is in auto mode. + */ + public ThermostatRunningModeEnum thermostatRunningMode; // 30 ThermostatRunningModeEnum R V + /** + * Indicates the day of the week that this thermostat considers to be the start of week for weekly setpoint + * scheduling. + * This attribute may be able to be used as the base to determine if the device supports weekly scheduling by + * reading the attribute. Successful response means that the weekly scheduling is supported. + */ + public StartOfWeekEnum startOfWeek; // 32 StartOfWeekEnum R V + /** + * Indicates how many weekly schedule transitions the thermostat is capable of handling. + */ + public Integer numberOfWeeklyTransitions; // 33 uint8 R V + /** + * Indicates how many daily schedule transitions the thermostat is capable of handling. + */ + public Integer numberOfDailyTransitions; // 34 uint8 R V + /** + * Indicates the temperature hold status on the thermostat. If hold status is on, the thermostat SHOULD maintain the + * temperature setpoint for the current mode until a system mode change. If hold status is off, the thermostat + * SHOULD follow the setpoint transitions specified by its internal scheduling program. If the thermostat supports + * setpoint hold for a specific duration, it SHOULD also implement the TemperatureSetpointHoldDuration attribute. + * If the server supports a setpoint hold for a specific duration, it SHOULD also implement the + * SetpointHoldExpiryTimestamp attribute. + * If this attribute is updated to SetpointHoldOn and the TemperatureSetpointHoldDuration has a non-null value and + * the SetpointHoldExpiryTimestamp is supported, the server shall update the SetpointHoldExpiryTimestamp with a + * value of current UTC timestamp, in seconds, plus the value in TemperatureSetpointHoldDuration multiplied by 60. + * If this attribute is updated to SetpointHoldOff and the SetpointHoldExpiryTimestamp is supported, the server + * shall set the SetpointHoldExpiryTimestamp to null. + */ + public TemperatureSetpointHoldEnum temperatureSetpointHold; // 35 TemperatureSetpointHoldEnum RW VM + /** + * Indicates the period in minutes for which a setpoint hold is active. Thermostats that support hold for a + * specified duration SHOULD implement this attribute. The null value indicates the field is unused. All other + * values are reserved. + * If this attribute is updated to a non-null value and the TemperatureSetpointHold is set to SetpointHoldOn and the + * SetpointHoldExpiryTimestamp is supported, the server shall update SetpointHoldExpiryTimestamp with a value of + * current UTC timestamp, in seconds, plus the new value of this attribute multiplied by 60. + * If this attribute is set to null and the SetpointHoldExpiryTimestamp is supported, the server shall set the + * SetpointHoldExpiryTimestamp to null. + */ + public Integer temperatureSetpointHoldDuration; // 36 uint16 RW VM + /** + * Indicates the operational state of the thermostat’s programming. The thermostat shall modify its programming + * operation when this attribute is modified by a client and update this attribute when its programming operation is + * modified locally by a user. The thermostat may support more than one active ProgrammingOperationModeBitmap. For + * example, the thermostat may operate simultaneously in Schedule Programming Mode and Recovery Mode. + * Thermostats which contain a schedule may use this attribute to control how that schedule is used, even if they do + * not support the ScheduleConfiguration feature. + * When ScheduleActive is not set, the setpoint is altered only by manual up/down changes at the thermostat or + * remotely, not by internal schedule programming. + * > [!NOTE] + * > Modifying the ScheduleActive bit does not clear or delete previous weekly schedule programming + * configurations. + */ + public ProgrammingOperationModeBitmap thermostatProgrammingOperationMode; // 37 ProgrammingOperationModeBitmap RW VM + /** + * Indicates the current relay state of the heat, cool, and fan relays. Unimplemented outputs shall be treated as if + * they were Off. + */ + public RelayStateBitmap thermostatRunningState; // 41 RelayStateBitmap R V + /** + * Indicates the source of the current active OccupiedCoolingSetpoint or OccupiedHeatingSetpoint (i.e., who or what + * determined the current setpoint). + * This attribute enables service providers to determine whether changes to setpoints were initiated due to occupant + * comfort, scheduled programming or some other source (e.g., electric utility or other service provider). Because + * automation services may initiate frequent setpoint changes, this attribute clearly differentiates the source of + * setpoint changes made at the thermostat. + */ + public SetpointChangeSourceEnum setpointChangeSource; // 48 SetpointChangeSourceEnum R V + /** + * Indicates the delta between the current active OccupiedCoolingSetpoint or OccupiedHeatingSetpoint and the + * previous active setpoint. This attribute is meant to accompany the SetpointChangeSource attribute; devices + * implementing SetpointChangeAmount SHOULD also implement SetpointChangeSource. + * The null value indicates that the previous setpoint was unknown. + */ + public Integer setpointChangeAmount; // 49 TemperatureDifference R V + /** + * Indicates the time in UTC at which the SetpointChangeAmount attribute change was recorded. + */ + public Integer setpointChangeSourceTimestamp; // 50 epoch-s R V + /** + * Indicates the amount that the Thermostat server will allow the Calculated Local Temperature to float above the + * OccupiedCoolingSetpoint (i.e., OccupiedCoolingSetpoint + OccupiedSetback) or below the OccupiedHeatingSetpoint + * setpoint (i.e., OccupiedHeatingSetpoint – OccupiedSetback) before initiating a state change to bring the + * temperature back to the user’s + * desired setpoint. This attribute is sometimes also referred to as the “span.” + * The purpose of this attribute is to allow remote configuration of the span between the desired setpoint and the + * measured temperature to help prevent over-cycling and reduce energy bills, though this may result in lower + * comfort on the part of some users. + * The null value indicates the attribute is unused. + * If the Thermostat client attempts to write OccupiedSetback to a value greater than OccupiedSetbackMax, the + * Thermostat server shall set its OccupiedSetback value to OccupiedSetbackMax and shall send a Write Attribute + * Response command with a Status Code field enumeration of SUCCESS response. + * If the Thermostat client attempts to write OccupiedSetback to a value less than OccupiedSetbackMin, the + * Thermostat server shall set its OccupiedSetback value to OccupiedSetbackMin and shall send a Write Attribute + * Response command with a Status Code field enumeration of SUCCESS response. + */ + public Integer occupiedSetback; // 52 UnsignedTemperature RW VM + /** + * Indicates the minimum value that the Thermostat server will allow the OccupiedSetback attribute to be configured + * by a user. + * The null value indicates the attribute is unused. + */ + public Integer occupiedSetbackMin; // 53 UnsignedTemperature R V + /** + * Indicates the maximum value that the Thermostat server will allow the OccupiedSetback attribute to be configured + * by a user. + * The null value indicates the attribute is unused. + */ + public Integer occupiedSetbackMax; // 54 UnsignedTemperature R V + /** + * Indicates the amount that the Thermostat server will allow the Calculated Local Temperature to float above the + * UnoccupiedCoolingSetpoint (i.e., UnoccupiedCoolingSetpoint + UnoccupiedSetback) or below the + * UnoccupiedHeatingSetpoint setpoint (i.e., UnoccupiedHeatingSetpoint - UnoccupiedSetback) before initiating a + * state change to bring the temperature back to the user’s desired setpoint. This attribute is sometimes also + * referred to as the “span.” + * The purpose of this attribute is to allow remote configuration of the span between the desired setpoint and the + * measured temperature to help prevent over-cycling and reduce energy bills, though this may result in lower + * comfort on the part of some users. + * The null value indicates the attribute is unused. + * If the Thermostat client attempts to write UnoccupiedSetback to a value greater than UnoccupiedSetbackMax, the + * Thermostat server shall set its UnoccupiedSetback value to UnoccupiedSetbackMax and shall send a Write Attribute + * Response command with a Status Code field enumeration of SUCCESS response. + * If the Thermostat client attempts to write UnoccupiedSetback to a value less than UnoccupiedSetbackMin, the + * Thermostat server shall set its UnoccupiedSetback value to UnoccupiedSetbackMin and shall send a Write Attribute + * Response command with a Status Code field enumeration of SUCCESS response. + */ + public Integer unoccupiedSetback; // 55 UnsignedTemperature RW VM + /** + * Indicates the minimum value that the Thermostat server will allow the UnoccupiedSetback attribute to be + * configured by a user. + * The null value indicates the attribute is unused. + */ + public Integer unoccupiedSetbackMin; // 56 UnsignedTemperature R V + /** + * Indicates the maximum value that the Thermostat server will allow the UnoccupiedSetback attribute to be + * configured by a user. + * The null value indicates the attribute is unused. + */ + public Integer unoccupiedSetbackMax; // 57 UnsignedTemperature R V + /** + * Indicates the delta between the Calculated Local Temperature and the OccupiedHeatingSetpoint or + * UnoccupiedHeatingSetpoint attributes at which the Thermostat server will operate in emergency heat mode. + * If the difference between the Calculated Local Temperature and OccupiedCoolingSetpoint or + * UnoccupiedCoolingSetpoint is greater than or equal to the EmergencyHeatDelta and the Thermostat server’s + * SystemMode attribute is in a heating-related mode, then the Thermostat server shall immediately switch to the + * SystemMode attribute value that provides the highest stage of heating (e.g., emergency heat) and continue + * operating in that running state until the OccupiedHeatingSetpoint value is reached. For example: + * • Calculated Local Temperature = 10.0°C + * • OccupiedHeatingSetpoint = 16.0°C + * • EmergencyHeatDelta = 2.0°C + * ⇒ OccupiedHeatingSetpoint - Calculated Local Temperature ≥? EmergencyHeatDelta + * ⇒ 16°C - 10°C ≥? 2°C + * ⇒ TRUE >>> Thermostat server changes its SystemMode to operate in 2nd stage or emergency heat mode + * The purpose of this attribute is to provide Thermostat clients the ability to configure rapid heating when a + * setpoint is of a specified amount greater than the measured temperature. This allows the heated space to be + * quickly heated to the desired level set by the user. + */ + public Integer emergencyHeatDelta; // 58 UnsignedTemperature RW VM + /** + * Indicates the type of Mini Split ACTypeEnum of Mini Split AC is defined depending on how Cooling and Heating + * condition is achieved by Mini Split AC. + */ + public ACTypeEnum acType; // 64 ACTypeEnum RW VM + /** + * Indicates capacity of Mini Split AC in terms of the format defined by the ACCapacityFormat attribute + */ + public Integer acCapacity; // 65 uint16 RW VM + /** + * Indicates type of refrigerant used within the Mini Split AC. + */ + public ACRefrigerantTypeEnum acRefrigerantType; // 66 ACRefrigerantTypeEnum RW VM + /** + * Indicates the type of compressor used within the Mini Split AC. + */ + public ACCompressorTypeEnum acCompressorType; // 67 ACCompressorTypeEnum RW VM + /** + * Indicates the type of errors encountered within the Mini Split AC. + */ + public ACErrorCodeBitmap acErrorCode; // 68 ACErrorCodeBitmap RW VM + /** + * Indicates the position of Louver on the AC. + */ + public ACLouverPositionEnum acLouverPosition; // 69 ACLouverPositionEnum RW VM + /** + * Indicates the temperature of the AC coil, as measured locally or remotely (over the network). + */ + public Integer acCoilTemperature; // 70 temperature R V + /** + * Indicates the format for the ACCapacity attribute. + */ + public ACCapacityFormatEnum acCapacityFormat; // 71 ACCapacityFormatEnum RW VM + /** + * Indicates the supported PresetScenarioEnum values, limits on how many presets can be created for each + * PresetScenarioEnum, and whether or not a thermostat can transition automatically to a given scenario. + */ + public List presetTypes; // 72 list R V + /** + * Indicates the supported SystemMode values for Schedules, limits on how many schedules can be created for each + * SystemMode value, and whether or not a given SystemMode value supports transitions to Presets, target setpoints, + * or both. + */ + public List scheduleTypes; // 73 list R V + /** + * Indicates the maximum number of entries supported by the Presets attribute. + */ + public Integer numberOfPresets; // 74 uint8 R V + /** + * Indicates the maximum number of entries supported by the Schedules attribute. + */ + public Integer numberOfSchedules; // 75 uint8 R V + /** + * Indicates the maximum number of transitions per Schedules attribute entry. + */ + public Integer numberOfScheduleTransitions; // 76 uint8 R V + public Integer numberOfScheduleTransitionPerDay; // 77 uint8 R V + /** + * Indicates the PresetHandle of the active preset. If this attribute is null, then there is no active preset. + */ + public OctetString activePresetHandle; // 78 octstr R V + /** + * Indicates the ScheduleHandle of the active schedule. A null value in this attribute indicates that there is no + * active schedule. + */ + public OctetString activeScheduleHandle; // 79 octstr R V + /** + * This attribute shall contain the current list of configured presets. On receipt of a write request: + * 1. If the PresetHandle field is null, the PresetStruct shall be treated as an added preset, and the device shall + * create a new unique value for the PresetHandle field. + * a. If the BuiltIn field is true, a response with the status code CONSTRAINT_ERROR shall be returned. + * 2. If the PresetHandle field is not null, the PresetStruct shall be treated as a modification of an existing + * preset. + * a. If the value of the PresetHandle field does not match any of the existing presets, a response with the status + * code NOT_FOUND shall be returned. + * b. If the value of the PresetHandle field is duplicated on multiple presets in the updated list, a response with + * the status code CONSTRAINT_ERROR shall be returned. + * c. If the BuiltIn field is true, and the PresetStruct in the current value with a matching PresetHandle field has + * a BuiltIn field set to false, a response with the status code CONSTRAINT_ERROR shall be returned. + * d. If the BuiltIn field is false, and the PresetStruct in the current value with a matching PresetHandle field + * has a BuiltIn field set to true, a response with the status code CONSTRAINT_ERROR shall be returned. + * 3. If the specified PresetScenarioEnum value does not exist in PresetTypes, a response with the status code + * CONSTRAINT_ERROR shall be returned. + * 4. If the Name is set, but the associated PresetTypeStruct does not have the SupportsNames bit set, a response + * with the status code CONSTRAINT_ERROR shall be returned. + * 5. If appending the received PresetStruct to the pending list of Presets would cause the total number of pending + * presets to exceed the value of the NumberOfPresets attribute, a response with the status code RESOURCE_EXHAUSTED + * shall be returned. + * 6. If appending the received PresetStruct to the pending list of Presets would cause the total number of pending + * presets whose PresetScenario field matches the appended preset’s PresetScenario field to exceed the value of the + * NumberOfPresets field on the PresetTypeStruct whose PresetScenario matches the appended preset’s PresetScenario + * field, a response with the status code RESOURCE_EXHAUSTED shall be returned. + * 7. Otherwise, the write shall be pended until receipt of a commit request, and the status code SUCCESS shall be + * returned. + * a. If the BuiltIn field is null: + * i. If there is a PresetStruct in the current value with a matching PresetHandle field, the BuiltIn field on the + * pending PresetStruct shall be set to the value of the BuiltIn on the matching PresetStruct. + * ii. Otherwise, the BuiltIn field on the pending PresetStruct shall be set to false. + * On an attempt to commit, the status of this attribute shall be determined as follows: + * 1. For all existing presets: + * a. If, after applying all pending changes, the updated value of the Presets attribute would not contain a + * PresetStruct with a matching PresetHandle field, indicating the removal of the PresetStruct, the server shall + * check for invalid removal of the PresetStruct: + * i. If the BuiltIn field is true on the removed PresetStruct, the attribute status shall be CONSTRAINT_ERROR. + * ii. If the MSCH feature is supported and the removed PresetHandle would be referenced by any PresetHandle on any + * ScheduleTransitionStruct on any ScheduleStruct in the updated value of the Schedules attribute, the attribute + * status shall be INVALID_IN_STATE. + * iii. If the removed PresetHandle is equal to the value of the ActivePresetHandle attribute, the attribute status + * shall be INVALID_IN_STATE. + * 2. Otherwise, the attribute status shall be SUCCESS. + */ + public List presets; // 80 list RW VM + /** + * This attribute shall contain a list of ScheduleStructs. On receipt of a write request: + * 1. For all schedules in the write request: + * a. If the ScheduleHandle field is null, the ScheduleStruct shall be treated as an added schedule, and the device + * shall create a new unique value for the ScheduleHandle field. + * i. If the BuiltIn field is true, a response with the status code CONSTRAINT_ERROR shall be returned. + * b. Otherwise, if the ScheduleHandle field is not null, the ScheduleStruct shall be treated as a modification of + * an existing schedule. + * i. If the value of the ScheduleHandle field does not match any of the existing schedules, a response with the + * status code NOT_FOUND shall be returned. + * ii. If the BuiltIn field is true, and the ScheduleStruct in the current value with a matching ScheduleHandle + * field has a BuiltIn field set to false, a response with the status code CONSTRAINT_ERROR shall be returned. + * iii. If the BuiltIn field is false, and the ScheduleStruct in the current value with a matching ScheduleHandle + * field has a BuiltIn field set to true, a response with the status code CONSTRAINT_ERROR shall be returned. + * c. If the specified SystemMode does not exist in ScheduleTypes, a response with the status code CONSTRAINT_ERROR + * shall be returned. + * d. If the number of transitions exceeds the NumberOfScheduleTransitions value, a response with the status code + * RESOURCE_EXHAUSTED shall be returned. + * e. If the value of the NumberOfScheduleTransitionsPerDay attribute is not null, and the number of transitions on + * any single day of the week exceeds the NumberOfScheduleTransitionsPerDay value, a response with the status code + * RESOURCE_EXHAUSTED shall be returned. + * f. If the PresetHandle field is present, but the associated ScheduleTypeStruct does not have the SupportsPresets + * bit set, a response with the status code CONSTRAINT_ERROR shall be returned. + * g. If the PresetHandle field is present, but after applying all pending changes, the Presets attribute would not + * contain a PresetStruct whose PresetHandle field matches the value of the PresetHandle field, a response with the + * status code CONSTRAINT_ERROR shall be returned. + * h. If the Name is set, but the associated ScheduleTypeStruct does not have the SupportsNames bit set, a response + * with the status code CONSTRAINT_ERROR shall be returned. + * i. For all transitions in all schedules in the write request: + * i. If the PresetHandle field is present, but the ScheduleTypeStruct matching the value of the SystemMode field on + * the encompassing ScheduleStruct does not have the SupportsPresets bit set, a response with the status code + * CONSTRAINT_ERROR shall be returned. + * j. If the PresetHandle field is present, but after applying all pending changes, the Presets attribute would not + * contain a PresetStruct whose PresetHandle field matches the value of the PresetHandle field, a response with the + * status code CONSTRAINT_ERROR shall be returned. + * i. If the SystemMode field is present, but the ScheduleTypeStruct matching the value of the SystemMode field on + * the encompassing ScheduleStruct does not have the SupportsSetpoints bit set, a response with the status code + * CONSTRAINT_ERROR shall be returned. + * ii. If the SystemMode field is has a value of SystemModeOff, but the ScheduleTypeStruct matching the value of the + * SystemMode field on the encompassing ScheduleStruct does not have the SupportsOff bit set, a response with the + * status code CONSTRAINT_ERROR shall be returned. + * k. If the HeatingSetpoint field is present, but the ScheduleTypeStruct matching the value of the SystemMode field + * on the encompassing ScheduleStruct does not have the SupportsSetpoints bit set, a response with the status code + * CONSTRAINT_ERROR shall be returned. + * l. If the CoolingSetpoint field is present, but the ScheduleTypeStruct matching the value of the SystemMode field + * on the encompassing ScheduleStruct does not have the SupportsSetpoints bit set, a response with the status code + * CONSTRAINT_ERROR shall be returned. + * 2. If appending the received ScheduleStruct to the pending list of Schedules would cause the total number of + * pending schedules to exceed the value of the NumberOfSchedules attribute, a response with the status code + * RESOURCE_EXHAUSTED shall be returned. + * 3. If appending the received ScheduleStruct to the pending list of Schedules would cause the total number of + * pending schedules whose SystemMode field matches the appended schedule’s SystemMode field to exceed the value of + * the NumberOfSchedules field on the ScheduleTypeStruct whose SystemMode field matches the appended schedule’s + * SystemMode field, a response with the status code RESOURCE_EXHAUSTED shall be returned. + * 4. Otherwise, the write shall be pended until receipt of a commit request, and the attribute status shall be + * SUCCESS. + * a. If the BuiltIn field is null: + * i. If there is a ScheduleStruct in the current value with a matching ScheduleHandle field, the BuiltIn field on + * the pending ScheduleStruct shall be set to the value of the BuiltIn on the matching ScheduleStruct. + * ii. Otherwise, the BuiltIn field on the pending ScheduleStruct shall be set to false. + * On an attempt to commit, the status of this attribute shall be determined as follows: + * 1. For all existing schedules: + * a. If, after applying all pending changes, the updated value of the Schedules attribute would not contain a + * ScheduleStruct with a matching ScheduleHandle field, indicating the removal of the ScheduleStruct, the server + * shall check for invalid removal of the ScheduleStruct: + * i. If the BuiltIn field is true on the removed ScheduleStruct, the attribute status shall be CONSTRAINT_ERROR. + * ii. If the removed ScheduleHandle is equal to the value of the ActiveScheduleHandle attribute, the attribute + * status shall be INVALID_IN_STATE. + * 2. Otherwise, the attribute status shall be SUCCESS. + */ + public List schedules; // 81 list RW VM + /** + * If there is a known time when the TemperatureSetpointHold shall be cleared, this attribute shall contain the + * timestamp in UTC indicating when that will happen. If there is no such known time, this attribute shall be null. + * If the TemperatureSetpointHold is set to SetpointHoldOff or the TemperatureSetpointHoldDuration is set to null, + * this attribute shall be set to null indicating there is no hold on the Thermostat either with or without a + * duration. + */ + public Integer setpointHoldExpiryTimestamp; // 82 epoch-s R V + // Structs + + public class PresetStruct { + /** + * This field shall indicate a device generated identifier for this preset. It shall be unique on the device, + * and shall NOT be reused after the associated preset has been deleted. + * This field shall only be null when the encompassing PresetStruct is appended to the Presets attribute for the + * purpose of creating a new Preset. Refer to Presets for the creation of Preset handles. + */ + public OctetString presetHandle; // octstr + /** + * This field shall indicate the associated PresetScenarioEnum value for this preset. + */ + public PresetScenarioEnum presetScenario; // PresetScenarioEnum + /** + * This field shall indicate a name provided by a user. The null value shall indicate no name. + * Within each subset of presets sharing the same PresetScenario field value, there shall NOT be any presets + * with the same value, including null as a value, in the Name field. + */ + public String name; // string + /** + * This field shall indicate the cooling setpoint for the preset. Refer to Setpoint Limits for value + * constraints. + */ + public Integer coolingSetpoint; // temperature + /** + * This field shall indicate the heating setpoint for the preset. Refer to Setpoint Limits for value + * constraints. + */ + public Integer heatingSetpoint; // temperature + /** + * This field shall indicate whether the preset is marked as "built-in", meaning that it can be + * modified, but it cannot be deleted. + */ + public Boolean builtIn; // bool + + public PresetStruct(OctetString presetHandle, PresetScenarioEnum presetScenario, String name, + Integer coolingSetpoint, Integer heatingSetpoint, Boolean builtIn) { + this.presetHandle = presetHandle; + this.presetScenario = presetScenario; + this.name = name; + this.coolingSetpoint = coolingSetpoint; + this.heatingSetpoint = heatingSetpoint; + this.builtIn = builtIn; + } + } + + public class PresetTypeStruct { + /** + * This field shall specify a PresetScenarioEnum value supported by this thermostat. + */ + public PresetScenarioEnum presetScenario; // PresetScenarioEnum + /** + * This field shall specify a limit for the number of presets for this PresetScenarioEnum. + */ + public Integer numberOfPresets; // uint8 + /** + * This field shall specify a bitmap of features for this PresetTypeStruct. + */ + public PresetTypeFeaturesBitmap presetTypeFeatures; // PresetTypeFeaturesBitmap + + public PresetTypeStruct(PresetScenarioEnum presetScenario, Integer numberOfPresets, + PresetTypeFeaturesBitmap presetTypeFeatures) { + this.presetScenario = presetScenario; + this.numberOfPresets = numberOfPresets; + this.presetTypeFeatures = presetTypeFeatures; + } + } + + /** + * This represents a single transition in a Thermostat schedule + */ + public class WeeklyScheduleTransitionStruct { + /** + * This field shall represent the start time of the schedule transition during the associated day. The time will + * be represented by a 16 bits unsigned integer to designate the minutes since midnight. For example, 6am will + * be represented by 360 minutes since midnight and 11:30pm will be represented by 1410 minutes since midnight. + */ + public Integer transitionTime; // uint16 + /** + * This field shall represent the heat setpoint to be applied at this associated transition start time. + */ + public Integer heatSetpoint; // temperature + /** + * This field shall represent the cool setpoint to be applied at this associated transition start time. + */ + public Integer coolSetpoint; // temperature + + public WeeklyScheduleTransitionStruct(Integer transitionTime, Integer heatSetpoint, Integer coolSetpoint) { + this.transitionTime = transitionTime; + this.heatSetpoint = heatSetpoint; + this.coolSetpoint = coolSetpoint; + } + } + + public class ScheduleStruct { + /** + * This field shall indicate a device generated identifier for this schedule. It shall be unique on the device, + * and shall NOT be reused after the associated schedule has been deleted. + * This field shall only be null when the encompassing ScheduleStruct is appended to the Schedules attribute for + * the purpose of creating a new Schedule. Refer to Schedules for the creation of Schedule handles. + */ + public OctetString scheduleHandle; // octstr + /** + * This field shall specify the default thermostat system mode for transitions in this schedule. The only valid + * values for this field shall be Auto, Heat, and Cool. + */ + public SystemModeEnum systemMode; // SystemModeEnum + /** + * This field shall specify a name for the ScheduleStruct. + */ + public String name; // string + /** + * This field shall indicate the default PresetHandle value for transitions in this schedule. + */ + public OctetString presetHandle; // octstr + /** + * This field shall specify a list of transitions for the schedule. + * This field shall NOT contain more than one ScheduleStruct with the same TransitionTime field and overlapping + * DayOfWeek fields; i.e. there shall be no duplicate transitions. + * If the NumberOfScheduleTransitionsPerDay attribute is not null, then for each bit in ScheduleDayOfWeekBitmap, + * the number of transitions with that bit set in DayOfWeek shall NOT be greater than the value of the + * NumberOfScheduleTransitionsPerDay attribute. + * For the purposes of determining which ScheduleStruct in this list is currently active, the current time shall + * be the number of minutes past midnight in the display value of the current time, not the actual number of + * minutes that have elapsed since midnight. On days which transition into or out of daylight saving time, + * certain values may repeat or not occur during the transition period. + * A ScheduleTransitionStruct in this list shall be active if the current day of the week matches its DayOfWeek + * field and the current time is greater than or equal to the TransitionTime, but less than the TransitionTime + * on any other ScheduleTransitionStruct in the Transitions field whose DayOfWeek field also matches the current + * day of the week. + * If the current time is less than every ScheduleTransitionStruct whose DayOfWeek field also matches the + * current day of the week, the server shall attempt the same process to identify the active + * ScheduleTransitionStruct for the day preceding the previously attempted day of the week, repeating until an + * active ScheduleTransitionStruct is found or the attempted day is the current day of the week again. If no + * active ScheduleTransitionStruct is found, then the active ScheduleTransitionStruct shall be the + * ScheduleTransitionStruct with the largest TransitionTime field from the set of ScheduleTransitionStructs + * whose DayOfWeek field matches the current day of the week. + */ + public List transitions; // list + /** + * This field shall indicate whether the schedule is marked as "built-in", meaning that it can be + * modified, but it cannot be deleted. + */ + public Boolean builtIn; // bool + + public ScheduleStruct(OctetString scheduleHandle, SystemModeEnum systemMode, String name, + OctetString presetHandle, List transitions, Boolean builtIn) { + this.scheduleHandle = scheduleHandle; + this.systemMode = systemMode; + this.name = name; + this.presetHandle = presetHandle; + this.transitions = transitions; + this.builtIn = builtIn; + } + } + + /** + * This struct provides a time of day and a set of days of the week for a state transition within a schedule. The + * thermostat shall use the following order of precedence for determining a new setpoint at the time of transition: + * 1. If the PresetHandle field is provided, then the setpoint for the PresetStruct in the Presets attribute with + * that identifier shall be used + * 2. If either the HeatingSetpoint or CoolingSetpoint is provided, then it shall be used + * a. If the SystemMode field is provided, the HeatingSetpoint and CoolingSetpoint fields shall be interpreted using + * the SystemMode field + * b. If the SystemMode field is not provided, the HeatingSetpoint and CoolingSetpoint fields shall be interpreted + * using the SystemMode field on the parent ScheduleStruct + * 3. If neither the PresetHandle field or any Setpoint field is provided, then the PresetHandle field on the parent + * ScheduleStruct shall be used to determine the active PresetStruct + * 4. If the PresetHandle is not indicated and no setpoint is provided for the current SystemMode, the server shall + * use a default value for the current SystemMode. + * If the setpoint was derived from a preset, then the ActivePresetHandle shall be set to the PresetHandle of that + * preset. + * If a CoolingSetpoint was used to determine the cooling setpoint: + * • If the server supports the OCC feature, and the Occupied bit is not set on the Occupancy attribute, then the + * UnoccupiedCoolingSetpoint attribute shall be set to the CoolingSetpoint + * • Otherwise, the OccupiedCoolingSetpoint attribute shall be set to the CoolingSetpoint If a HeatingSetpoint was + * used to determine the heating setpoint: + * • If the server supports the OCC feature, and the Occupied bit is not set on the Occupancy attribute, then the + * UnoccupiedHeatingSetpoint attribute shall be set to the HeatingSetpoint + * • Otherwise, the OccupiedHeatingSetpoint attribute shall be set to the HeatingSetpoint The + * ScheduleTransitionStruct shall be invalid if all the following are true: + * • The HeatingSetpoint field is not provided + * • The PresetHandle field is not provided + * • The PresetHandle field on the encompassing ScheduleStruct is not provided + * • The SystemMode field is provided and has the value Heat or Auto, or the SystemMode field on the parent + * ScheduleStruct has the value Heat or Auto + * The ScheduleTransitionStruct shall be invalid if all the following are true: + * • The CoolingSetpoint field is not provided + * • The PresetHandle field is not provided + * • The PresetHandle field on the encompassing ScheduleStruct is not provided + * • The SystemMode field is provided and has the value Cool or Auto, or the SystemMode field on the parent + * ScheduleStruct has the value Cool or Auto + */ + public class ScheduleTransitionStruct { + /** + * This field shall specify a bitmask of days of the week that the transition applies to. The Vacation bit shall + * NOT be set; vacation schedules shall be set via the vacation preset. + */ + public ScheduleDayOfWeekBitmap dayOfWeek; // ScheduleDayOfWeekBitmap + /** + * This shall specify the time of day at which the transition becomes active, in terms of minutes within the day + * representing the wall clock, where 0 is 00:00:00, 1 is 00:01:00 and 1439 is 23:59:00. + * Handling of transitions during the changeover of Daylight Saving Time is implementation-dependent. + */ + public Integer transitionTime; // uint16 + /** + * This field shall specify the preset used at the TransitionTime. If this field is provided, then the + * SystemMode, CoolingSetpoint and HeatingSetpoint fields shall NOT be provided. + */ + public OctetString presetHandle; // octstr + /** + * This shall specify the default mode to which the thermostat will switch for this transition, overriding the + * default for the schedule. The only valid values for this field shall be Auto, Heat, Cool and Off. This field + * shall only be included when the required system mode differs from the schedule’s default SystemMode. + */ + public SystemModeEnum systemMode; // SystemModeEnum + /** + * This field shall specify the cooling setpoint for the transition. If PresetHandle is set, this field shall + * NOT be included. Refer to Setpoint Limits for value constraints. + */ + public Integer coolingSetpoint; // temperature + /** + * This field shall specify the cooling setpoint for the transition. If PresetHandle is set, this field shall + * NOT be included. Refer to Setpoint Limits for value constraints. + */ + public Integer heatingSetpoint; // temperature + + public ScheduleTransitionStruct(ScheduleDayOfWeekBitmap dayOfWeek, Integer transitionTime, + OctetString presetHandle, SystemModeEnum systemMode, Integer coolingSetpoint, Integer heatingSetpoint) { + this.dayOfWeek = dayOfWeek; + this.transitionTime = transitionTime; + this.presetHandle = presetHandle; + this.systemMode = systemMode; + this.coolingSetpoint = coolingSetpoint; + this.heatingSetpoint = heatingSetpoint; + } + } + + public class ScheduleTypeStruct { + /** + * This field shall specify a SystemModeEnum supported by this thermostat for Schedules. The only valid values + * for this field shall be Auto, Heat, and Cool. + */ + public SystemModeEnum systemMode; // SystemModeEnum + /** + * This field shall specify a limit for the number of Schedules for this SystemMode. + */ + public Integer numberOfSchedules; // uint8 + /** + * This field shall specify a bitmap of features for this schedule entry. At least one of SupportsPresets and + * SupportsSetpoints shall be set. + */ + public ScheduleTypeFeaturesBitmap scheduleTypeFeatures; // ScheduleTypeFeaturesBitmap + + public ScheduleTypeStruct(SystemModeEnum systemMode, Integer numberOfSchedules, + ScheduleTypeFeaturesBitmap scheduleTypeFeatures) { + this.systemMode = systemMode; + this.numberOfSchedules = numberOfSchedules; + this.scheduleTypeFeatures = scheduleTypeFeatures; + } + } + + // Enums + public enum ACCapacityFormatEnum implements MatterEnum { + BT_UH(0, "Bt Uh"); + + public final Integer value; + public final String label; + + private ACCapacityFormatEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ACCompressorTypeEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + T1(1, "T 1"), + T2(2, "T 2"), + T3(3, "T 3"); + + public final Integer value; + public final String label; + + private ACCompressorTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ACLouverPositionEnum implements MatterEnum { + CLOSED(1, "Closed"), + OPEN(2, "Open"), + QUARTER(3, "Quarter"), + HALF(4, "Half"), + THREE_QUARTERS(5, "Three Quarters"); + + public final Integer value; + public final String label; + + private ACLouverPositionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ACRefrigerantTypeEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + R22(1, "R 22"), + R410A(2, "R 410 A"), + R407C(3, "R 407 C"); + + public final Integer value; + public final String label; + + private ACRefrigerantTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ACTypeEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + COOLING_FIXED(1, "Cooling Fixed"), + HEAT_PUMP_FIXED(2, "Heat Pump Fixed"), + COOLING_INVERTER(3, "Cooling Inverter"), + HEAT_PUMP_INVERTER(4, "Heat Pump Inverter"); + + public final Integer value; + public final String label; + + private ACTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SetpointRaiseLowerModeEnum implements MatterEnum { + HEAT(0, "Heat"), + COOL(1, "Cool"), + BOTH(2, "Both"); + + public final Integer value; + public final String label; + + private SetpointRaiseLowerModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * > [!NOTE] + * > CoolingAndHeating + * A thermostat indicating it supports CoolingAndHeating (or CoolingAndHeatingWithReheat) SHOULD be able to request + * heating or cooling on demand and will usually support the Auto SystemMode. + * Systems which support cooling or heating, requiring external intervention to change modes or where the whole + * building must be in the same mode, SHOULD report CoolingOnly or HeatingOnly based on the current capability. + */ + public enum ControlSequenceOfOperationEnum implements MatterEnum { + COOLING_ONLY(0, "Cooling Only"), + COOLING_WITH_REHEAT(1, "Cooling With Reheat"), + HEATING_ONLY(2, "Heating Only"), + HEATING_WITH_REHEAT(3, "Heating With Reheat"), + COOLING_AND_HEATING(4, "Cooling And Heating"), + COOLING_AND_HEATING_WITH_REHEAT(5, "Cooling And Heating With Reheat"); + + public final Integer value; + public final String label; + + private ControlSequenceOfOperationEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum PresetScenarioEnum implements MatterEnum { + OCCUPIED(1, "Occupied"), + UNOCCUPIED(2, "Unoccupied"), + SLEEP(3, "Sleep"), + WAKE(4, "Wake"), + VACATION(5, "Vacation"), + GOING_TO_SLEEP(6, "Going To Sleep"), + USER_DEFINED(254, "User Defined"); + + public final Integer value; + public final String label; + + private PresetScenarioEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum SetpointChangeSourceEnum implements MatterEnum { + MANUAL(0, "Manual"), + SCHEDULE(1, "Schedule"), + EXTERNAL(2, "External"); + + public final Integer value; + public final String label; + + private SetpointChangeSourceEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StartOfWeekEnum implements MatterEnum { + SUNDAY(0, "Sunday"), + MONDAY(1, "Monday"), + TUESDAY(2, "Tuesday"), + WEDNESDAY(3, "Wednesday"), + THURSDAY(4, "Thursday"), + FRIDAY(5, "Friday"), + SATURDAY(6, "Saturday"); + + public final Integer value; + public final String label; + + private StartOfWeekEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * Table 9. Interpretation of Heat, Cool and Auto SystemModeEnum Values + */ + public enum SystemModeEnum implements MatterEnum { + OFF(0, "Off"), + AUTO(1, "Auto"), + COOL(3, "Cool"), + HEAT(4, "Heat"), + EMERGENCY_HEAT(5, "Emergency Heat"), + PRECOOLING(6, "Precooling"), + FAN_ONLY(7, "Fan Only"), + DRY(8, "Dry"), + SLEEP(9, "Sleep"); + + public final Integer value; + public final String label; + + private SystemModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ThermostatRunningModeEnum implements MatterEnum { + OFF(0, "Off"), + COOL(3, "Cool"), + HEAT(4, "Heat"); + + public final Integer value; + public final String label; + + private ThermostatRunningModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum TemperatureSetpointHoldEnum implements MatterEnum { + SETPOINT_HOLD_OFF(0, "Setpoint Hold Off"), + SETPOINT_HOLD_ON(1, "Setpoint Hold On"); + + public final Integer value; + public final String label; + + private TemperatureSetpointHoldEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class ACErrorCodeBitmap { + public boolean compressorFail; + public boolean roomSensorFail; + public boolean outdoorSensorFail; + public boolean coilSensorFail; + public boolean fanFail; + + public ACErrorCodeBitmap(boolean compressorFail, boolean roomSensorFail, boolean outdoorSensorFail, + boolean coilSensorFail, boolean fanFail) { + this.compressorFail = compressorFail; + this.roomSensorFail = roomSensorFail; + this.outdoorSensorFail = outdoorSensorFail; + this.coilSensorFail = coilSensorFail; + this.fanFail = fanFail; + } + } + + public static class AlarmCodeBitmap { + public boolean initialization; + public boolean hardware; + public boolean selfCalibration; + + public AlarmCodeBitmap(boolean initialization, boolean hardware, boolean selfCalibration) { + this.initialization = initialization; + this.hardware = hardware; + this.selfCalibration = selfCalibration; + } + } + + public static class HVACSystemTypeBitmap { + /** + * Stage of cooling the HVAC system is using. + * These bits shall indicate what stage of cooling the HVAC system is using. + * • 00 = Cool Stage 1 + * • 01 = Cool Stage 2 + * • 10 = Cool Stage 3 + * • 11 = Reserved + */ + public short coolingStage; + /** + * Stage of heating the HVAC system is using. + * These bits shall indicate what stage of heating the HVAC system is using. + * • 00 = Heat Stage 1 + * • 01 = Heat Stage 2 + * • 10 = Heat Stage 3 + * • 11 = Reserved + */ + public short heatingStage; + /** + * Is the heating type Heat Pump. + * This bit shall indicate whether the HVAC system is conventional or a heat pump. + * • 0 = Conventional + * • 1 = Heat Pump + */ + public boolean heatingIsHeatPump; + /** + * Does the HVAC system use fuel. + * This bit shall indicate whether the HVAC system uses fuel. + * • 0 = Does not use fuel + * • 1 = Uses fuel + */ + public boolean heatingUsesFuel; + + public HVACSystemTypeBitmap(short coolingStage, short heatingStage, boolean heatingIsHeatPump, + boolean heatingUsesFuel) { + this.coolingStage = coolingStage; + this.heatingStage = heatingStage; + this.heatingIsHeatPump = heatingIsHeatPump; + this.heatingUsesFuel = heatingUsesFuel; + } + } + + public static class OccupancyBitmap { + /** + * Indicates the occupancy state + * If this bit is set, it shall indicate the occupied state else if the bit if not set, it shall indicate the + * unoccupied state. + */ + public boolean occupied; + + public OccupancyBitmap(boolean occupied) { + this.occupied = occupied; + } + } + + public static class PresetTypeFeaturesBitmap { + public boolean automatic; + public boolean supportsNames; + + public PresetTypeFeaturesBitmap(boolean automatic, boolean supportsNames) { + this.automatic = automatic; + this.supportsNames = supportsNames; + } + } + + public static class ProgrammingOperationModeBitmap { + public boolean scheduleActive; + public boolean autoRecovery; + public boolean economy; + + public ProgrammingOperationModeBitmap(boolean scheduleActive, boolean autoRecovery, boolean economy) { + this.scheduleActive = scheduleActive; + this.autoRecovery = autoRecovery; + this.economy = economy; + } + } + + public static class RelayStateBitmap { + public boolean heat; + public boolean cool; + public boolean fan; + public boolean heatStage2; + public boolean coolStage2; + public boolean fanStage2; + public boolean fanStage3; + + public RelayStateBitmap(boolean heat, boolean cool, boolean fan, boolean heatStage2, boolean coolStage2, + boolean fanStage2, boolean fanStage3) { + this.heat = heat; + this.cool = cool; + this.fan = fan; + this.heatStage2 = heatStage2; + this.coolStage2 = coolStage2; + this.fanStage2 = fanStage2; + this.fanStage3 = fanStage3; + } + } + + public static class RemoteSensingBitmap { + public boolean localTemperature; + /** + * OutdoorTemperature is derived from a remote node + * This bit shall be supported if the OutdoorTemperature attribute is supported. + */ + public boolean outdoorTemperature; + public boolean occupancy; + + public RemoteSensingBitmap(boolean localTemperature, boolean outdoorTemperature, boolean occupancy) { + this.localTemperature = localTemperature; + this.outdoorTemperature = outdoorTemperature; + this.occupancy = occupancy; + } + } + + public static class ScheduleTypeFeaturesBitmap { + /** + * Supports presets + * This bit shall indicate that any ScheduleStruct with a SystemMode field whose value matches the SystemMode + * field on the encompassing ScheduleTypeStruct supports specifying presets on ScheduleTransitionStructs + * contained in its Transitions field. + */ + public boolean supportsPresets; + /** + * Supports setpoints + * This bit shall indicate that any ScheduleStruct with a SystemMode field whose value matches the SystemMode + * field on the encompassing ScheduleTypeStruct supports specifying setpoints on ScheduleTransitionStructs + * contained in its Transitions field. + */ + public boolean supportsSetpoints; + /** + * Supports user-provided names + * This bit shall indicate that any ScheduleStruct with a SystemMode field whose value matches the SystemMode + * field on the encompassing ScheduleTypeStruct supports setting the value of the Name field. + */ + public boolean supportsNames; + /** + * Supports transitioning to SystemModeOff + * This bit shall indicate that any ScheduleStruct with a SystemMode field whose value matches the SystemMode + * field on the encompassing ScheduleTypeStruct supports setting its SystemMode field to Off. + */ + public boolean supportsOff; + + public ScheduleTypeFeaturesBitmap(boolean supportsPresets, boolean supportsSetpoints, boolean supportsNames, + boolean supportsOff) { + this.supportsPresets = supportsPresets; + this.supportsSetpoints = supportsSetpoints; + this.supportsNames = supportsNames; + this.supportsOff = supportsOff; + } + } + + public static class ScheduleDayOfWeekBitmap { + public boolean sunday; + public boolean monday; + public boolean tuesday; + public boolean wednesday; + public boolean thursday; + public boolean friday; + public boolean saturday; + public boolean away; + + public ScheduleDayOfWeekBitmap(boolean sunday, boolean monday, boolean tuesday, boolean wednesday, + boolean thursday, boolean friday, boolean saturday, boolean away) { + this.sunday = sunday; + this.monday = monday; + this.tuesday = tuesday; + this.wednesday = wednesday; + this.thursday = thursday; + this.friday = friday; + this.saturday = saturday; + this.away = away; + } + } + + public static class ScheduleModeBitmap { + public boolean heatSetpointPresent; + public boolean coolSetpointPresent; + + public ScheduleModeBitmap(boolean heatSetpointPresent, boolean coolSetpointPresent) { + this.heatSetpointPresent = heatSetpointPresent; + this.coolSetpointPresent = coolSetpointPresent; + } + } + + public static class FeatureMap { + /** + * + * Thermostat is capable of managing a heating device + */ + public boolean heating; + /** + * + * Thermostat is capable of managing a cooling device + */ + public boolean cooling; + /** + * + * Supports Occupied and Unoccupied setpoints + */ + public boolean occupancy; + /** + * + * Supports remote configuration of a weekly schedule of setpoint transitions + */ + public boolean scheduleConfiguration; + /** + * + * Supports configurable setback (or span) + */ + public boolean setback; + /** + * + * Supports a System Mode of Auto + */ + public boolean autoMode; + /** + * + * This feature indicates that the Calculated Local Temperature used internally is unavailable to report + * externally, for example due to the temperature control being done by a separate subsystem which does not + * offer a view into the currently measured temperature, but allows setpoints to be provided. + */ + public boolean localTemperatureNotExposed; + /** + * + * Supports enhanced schedules + */ + public boolean matterScheduleConfiguration; + /** + * + * Thermostat supports setpoint presets + */ + public boolean presets; + + public FeatureMap(boolean heating, boolean cooling, boolean occupancy, boolean scheduleConfiguration, + boolean setback, boolean autoMode, boolean localTemperatureNotExposed, + boolean matterScheduleConfiguration, boolean presets) { + this.heating = heating; + this.cooling = cooling; + this.occupancy = occupancy; + this.scheduleConfiguration = scheduleConfiguration; + this.setback = setback; + this.autoMode = autoMode; + this.localTemperatureNotExposed = localTemperatureNotExposed; + this.matterScheduleConfiguration = matterScheduleConfiguration; + this.presets = presets; + } + } + + public ThermostatCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 513, "Thermostat"); + } + + protected ThermostatCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + public static ClusterCommand setpointRaiseLower(SetpointRaiseLowerModeEnum mode, Integer amount) { + Map map = new LinkedHashMap<>(); + if (mode != null) { + map.put("mode", mode); + } + if (amount != null) { + map.put("amount", amount); + } + return new ClusterCommand("setpointRaiseLower", map); + } + + /** + * This command is used to update the thermostat weekly setpoint schedule from a management system. If the + * thermostat already has a weekly setpoint schedule programmed, then it SHOULD replace each daily setpoint set as + * it receives the updates from the management system. For example, if the thermostat has 4 setpoints for every day + * of the week and is sent a SetWeeklySchedule command with one setpoint for Saturday then the thermostat SHOULD + * remove all 4 setpoints for Saturday and replace those with the updated setpoint but leave all other days + * unchanged. If the schedule is larger than what fits in one frame or contains more than 10 transitions, the + * schedule shall then be sent using multiple SetWeeklySchedule Commands. + */ + public static ClusterCommand setWeeklySchedule(Integer numberOfTransitionsForSequence, + ScheduleDayOfWeekBitmap dayOfWeekForSequence, ScheduleModeBitmap modeForSequence, + List transitions) { + Map map = new LinkedHashMap<>(); + if (numberOfTransitionsForSequence != null) { + map.put("numberOfTransitionsForSequence", numberOfTransitionsForSequence); + } + if (dayOfWeekForSequence != null) { + map.put("dayOfWeekForSequence", dayOfWeekForSequence); + } + if (modeForSequence != null) { + map.put("modeForSequence", modeForSequence); + } + if (transitions != null) { + map.put("transitions", transitions); + } + return new ClusterCommand("setWeeklySchedule", map); + } + + public static ClusterCommand getWeeklySchedule(ScheduleDayOfWeekBitmap daysToReturn, + ScheduleModeBitmap modeToReturn) { + Map map = new LinkedHashMap<>(); + if (daysToReturn != null) { + map.put("daysToReturn", daysToReturn); + } + if (modeToReturn != null) { + map.put("modeToReturn", modeToReturn); + } + return new ClusterCommand("getWeeklySchedule", map); + } + + /** + * This command is used to clear the weekly schedule. The Clear weekly schedule has no payload. + * Upon receipt, all transitions currently stored shall be cleared and a default response of SUCCESS shall be sent + * in response. There are no error responses to this command. + */ + public static ClusterCommand clearWeeklySchedule() { + return new ClusterCommand("clearWeeklySchedule"); + } + + public static ClusterCommand setActiveScheduleRequest(OctetString scheduleHandle) { + Map map = new LinkedHashMap<>(); + if (scheduleHandle != null) { + map.put("scheduleHandle", scheduleHandle); + } + return new ClusterCommand("setActiveScheduleRequest", map); + } + + public static ClusterCommand setActivePresetRequest(OctetString presetHandle) { + Map map = new LinkedHashMap<>(); + if (presetHandle != null) { + map.put("presetHandle", presetHandle); + } + return new ClusterCommand("setActivePresetRequest", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "localTemperature : " + localTemperature + "\n"; + str += "outdoorTemperature : " + outdoorTemperature + "\n"; + str += "occupancy : " + occupancy + "\n"; + str += "absMinHeatSetpointLimit : " + absMinHeatSetpointLimit + "\n"; + str += "absMaxHeatSetpointLimit : " + absMaxHeatSetpointLimit + "\n"; + str += "absMinCoolSetpointLimit : " + absMinCoolSetpointLimit + "\n"; + str += "absMaxCoolSetpointLimit : " + absMaxCoolSetpointLimit + "\n"; + str += "piCoolingDemand : " + piCoolingDemand + "\n"; + str += "piHeatingDemand : " + piHeatingDemand + "\n"; + str += "localTemperatureCalibration : " + localTemperatureCalibration + "\n"; + str += "occupiedCoolingSetpoint : " + occupiedCoolingSetpoint + "\n"; + str += "occupiedHeatingSetpoint : " + occupiedHeatingSetpoint + "\n"; + str += "unoccupiedCoolingSetpoint : " + unoccupiedCoolingSetpoint + "\n"; + str += "unoccupiedHeatingSetpoint : " + unoccupiedHeatingSetpoint + "\n"; + str += "minHeatSetpointLimit : " + minHeatSetpointLimit + "\n"; + str += "maxHeatSetpointLimit : " + maxHeatSetpointLimit + "\n"; + str += "minCoolSetpointLimit : " + minCoolSetpointLimit + "\n"; + str += "maxCoolSetpointLimit : " + maxCoolSetpointLimit + "\n"; + str += "minSetpointDeadBand : " + minSetpointDeadBand + "\n"; + str += "remoteSensing : " + remoteSensing + "\n"; + str += "controlSequenceOfOperation : " + controlSequenceOfOperation + "\n"; + str += "systemMode : " + systemMode + "\n"; + str += "thermostatRunningMode : " + thermostatRunningMode + "\n"; + str += "startOfWeek : " + startOfWeek + "\n"; + str += "numberOfWeeklyTransitions : " + numberOfWeeklyTransitions + "\n"; + str += "numberOfDailyTransitions : " + numberOfDailyTransitions + "\n"; + str += "temperatureSetpointHold : " + temperatureSetpointHold + "\n"; + str += "temperatureSetpointHoldDuration : " + temperatureSetpointHoldDuration + "\n"; + str += "thermostatProgrammingOperationMode : " + thermostatProgrammingOperationMode + "\n"; + str += "thermostatRunningState : " + thermostatRunningState + "\n"; + str += "setpointChangeSource : " + setpointChangeSource + "\n"; + str += "setpointChangeAmount : " + setpointChangeAmount + "\n"; + str += "setpointChangeSourceTimestamp : " + setpointChangeSourceTimestamp + "\n"; + str += "occupiedSetback : " + occupiedSetback + "\n"; + str += "occupiedSetbackMin : " + occupiedSetbackMin + "\n"; + str += "occupiedSetbackMax : " + occupiedSetbackMax + "\n"; + str += "unoccupiedSetback : " + unoccupiedSetback + "\n"; + str += "unoccupiedSetbackMin : " + unoccupiedSetbackMin + "\n"; + str += "unoccupiedSetbackMax : " + unoccupiedSetbackMax + "\n"; + str += "emergencyHeatDelta : " + emergencyHeatDelta + "\n"; + str += "acType : " + acType + "\n"; + str += "acCapacity : " + acCapacity + "\n"; + str += "acRefrigerantType : " + acRefrigerantType + "\n"; + str += "acCompressorType : " + acCompressorType + "\n"; + str += "acErrorCode : " + acErrorCode + "\n"; + str += "acLouverPosition : " + acLouverPosition + "\n"; + str += "acCoilTemperature : " + acCoilTemperature + "\n"; + str += "acCapacityFormat : " + acCapacityFormat + "\n"; + str += "presetTypes : " + presetTypes + "\n"; + str += "scheduleTypes : " + scheduleTypes + "\n"; + str += "numberOfPresets : " + numberOfPresets + "\n"; + str += "numberOfSchedules : " + numberOfSchedules + "\n"; + str += "numberOfScheduleTransitions : " + numberOfScheduleTransitions + "\n"; + str += "numberOfScheduleTransitionPerDay : " + numberOfScheduleTransitionPerDay + "\n"; + str += "activePresetHandle : " + activePresetHandle + "\n"; + str += "activeScheduleHandle : " + activeScheduleHandle + "\n"; + str += "presets : " + presets + "\n"; + str += "schedules : " + schedules + "\n"; + str += "setpointHoldExpiryTimestamp : " + setpointHoldExpiryTimestamp + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThermostatUserInterfaceConfigurationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThermostatUserInterfaceConfigurationCluster.java new file mode 100644 index 00000000000..a290db81689 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThermostatUserInterfaceConfigurationCluster.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * ThermostatUserInterfaceConfiguration + * + * @author Dan Cunningham - Initial contribution + */ +public class ThermostatUserInterfaceConfigurationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0204; + public static final String CLUSTER_NAME = "ThermostatUserInterfaceConfiguration"; + public static final String CLUSTER_PREFIX = "thermostatUserInterfaceConfiguration"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_TEMPERATURE_DISPLAY_MODE = "temperatureDisplayMode"; + public static final String ATTRIBUTE_KEYPAD_LOCKOUT = "keypadLockout"; + public static final String ATTRIBUTE_SCHEDULE_PROGRAMMING_VISIBILITY = "scheduleProgrammingVisibility"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the units of the temperature displayed on the thermostat screen. + */ + public TemperatureDisplayModeEnum temperatureDisplayMode; // 0 TemperatureDisplayModeEnum RW VO + /** + * Indicates the level of functionality that is available to the user via the keypad. + */ + public KeypadLockoutEnum keypadLockout; // 1 KeypadLockoutEnum RW VM + /** + * This attribute is used to hide the weekly schedule programming functionality or menu on a thermostat from a user + * to prevent local user programming of the weekly schedule. The schedule programming may still be performed via a + * remote interface, and the thermostat may operate in schedule programming mode. + * This attribute is designed to prevent local tampering with or disabling of schedules that may have been + * programmed by users or service providers via a more capable remote interface. The programming schedule shall + * continue to run even though it is not visible to the user locally at the thermostat. + */ + public ScheduleProgrammingVisibilityEnum scheduleProgrammingVisibility; // 2 ScheduleProgrammingVisibilityEnum RW VM + + // Enums + public enum TemperatureDisplayModeEnum implements MatterEnum { + CELSIUS(0, "Celsius"), + FAHRENHEIT(1, "Fahrenheit"); + + public final Integer value; + public final String label; + + private TemperatureDisplayModeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * The interpretation of the various levels is device-dependent. + */ + public enum KeypadLockoutEnum implements MatterEnum { + NO_LOCKOUT(0, "No Lockout"), + LOCKOUT1(1, "Lockout 1"), + LOCKOUT2(2, "Lockout 2"), + LOCKOUT3(3, "Lockout 3"), + LOCKOUT4(4, "Lockout 4"), + LOCKOUT5(5, "Lockout 5"); + + public final Integer value; + public final String label; + + private KeypadLockoutEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ScheduleProgrammingVisibilityEnum implements MatterEnum { + SCHEDULE_PROGRAMMING_PERMITTED(0, "Schedule Programming Permitted"), + SCHEDULE_PROGRAMMING_DENIED(1, "Schedule Programming Denied"); + + public final Integer value; + public final String label; + + private ScheduleProgrammingVisibilityEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public ThermostatUserInterfaceConfigurationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 516, "ThermostatUserInterfaceConfiguration"); + } + + protected ThermostatUserInterfaceConfigurationCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "temperatureDisplayMode : " + temperatureDisplayMode + "\n"; + str += "keypadLockout : " + keypadLockout + "\n"; + str += "scheduleProgrammingVisibility : " + scheduleProgrammingVisibility + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadBorderRouterManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadBorderRouterManagementCluster.java new file mode 100644 index 00000000000..8175031574e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadBorderRouterManagementCluster.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ThreadBorderRouterManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class ThreadBorderRouterManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0452; + public static final String CLUSTER_NAME = "ThreadBorderRouterManagement"; + public static final String CLUSTER_PREFIX = "threadBorderRouterManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_BORDER_ROUTER_NAME = "borderRouterName"; + public static final String ATTRIBUTE_BORDER_AGENT_ID = "borderAgentId"; + public static final String ATTRIBUTE_THREAD_VERSION = "threadVersion"; + public static final String ATTRIBUTE_INTERFACE_ENABLED = "interfaceEnabled"; + public static final String ATTRIBUTE_ACTIVE_DATASET_TIMESTAMP = "activeDatasetTimestamp"; + public static final String ATTRIBUTE_PENDING_DATASET_TIMESTAMP = "pendingDatasetTimestamp"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates a user-friendly name identifying the device model or product of the Border Router in MeshCOP (DNS-SD + * service name) as defined in the Thread specification, and has the following recommended format: + * <VendorName> <ProductName>._meshcop._udp. An example name would be ACME Border Router + * (74be)._meshcop._udp. + */ + public String borderRouterName; // 0 string R V + /** + * Indicates a 16-byte globally unique ID for a Thread Border Router device. This ID is manufacturer-specific, and + * it is created and managed by the border router’s implementation. + */ + public OctetString borderAgentId; // 1 octstr R V + /** + * Indicates the Thread version supported by the Thread interface configured by the cluster instance. + * The format shall match the value mapping defined in the "Version TLV" section of the Thread + * specification. For example, Thread 1.3.0 would have ThreadVersion set to 4. + */ + public Integer threadVersion; // 2 uint16 R V + /** + * Indicates whether the associated IEEE 802.15.4 Thread interface is enabled or disabled. + */ + public Boolean interfaceEnabled; // 3 bool R V + /** + * Null if the Thread Border Router has no dataset configured, otherwise it shall be the timestamp value extracted + * from the Active Dataset value configured by the Thread Node to which the border router is connected. This + * attribute shall be updated when a new Active dataset is configured on the Thread network to which the border + * router is connected. + */ + public BigInteger activeDatasetTimestamp; // 4 uint64 R V + /** + * Null if the Thread Border Router has no Pending dataset configured, otherwise it shall be the timestamp value + * extracted from the Pending Dataset value configured by the Thread Node to which the border router is connected. + * This attribute shall be updated when a new Pending dataset is configured on the Thread network to which the + * border router is connected. + */ + public BigInteger pendingDatasetTimestamp; // 5 uint64 R V + + // Bitmaps + public static class FeatureMap { + /** + * + * This feature shall indicate the ability of the Border Router to change its already configured PAN to another, + * by setting a pending dataset. + * > [!NOTE] + * > This feature flag can be used to protect an already-configured network from accidental configuration + * change, e.g. when the Thread Border Router serves non- Matter devices that do not support PAN change for an + * implementation-specific reason. + */ + public boolean panChange; + + public FeatureMap(boolean panChange) { + this.panChange = panChange; + } + } + + public ThreadBorderRouterManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1106, "ThreadBorderRouterManagement"); + } + + protected ThreadBorderRouterManagementCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command shall be used to request the active operational dataset of the Thread network to which the border + * router is connected. + * If the command is not executed via a CASE session, the command shall fail with a status code of + * UNSUPPORTED_ACCESS. + * If an internal error occurs, then this command shall fail with a FAILURE status code sent back to the initiator. + * Otherwise, this shall generate a DatasetResponse command. + */ + public static ClusterCommand getActiveDatasetRequest() { + return new ClusterCommand("getActiveDatasetRequest"); + } + + /** + * This command shall be used to request the pending dataset of the Thread network to which the border router is + * connected. + * If the command is not executed via a CASE session, the command shall fail with a status code of + * UNSUPPORTED_ACCESS. + * If an internal error occurs, then this command shall fail with a FAILURE status code sent back to the initiator. + * Otherwise, this shall generate a DatasetResponse command. + */ + public static ClusterCommand getPendingDatasetRequest() { + return new ClusterCommand("getPendingDatasetRequest"); + } + + /** + * This command shall be used to set the active Dataset of the Thread network to which the Border Router is + * connected, when there is no active dataset already. + */ + public static ClusterCommand setActiveDatasetRequest(OctetString activeDataset, BigInteger breadcrumb) { + Map map = new LinkedHashMap<>(); + if (activeDataset != null) { + map.put("activeDataset", activeDataset); + } + if (breadcrumb != null) { + map.put("breadcrumb", breadcrumb); + } + return new ClusterCommand("setActiveDatasetRequest", map); + } + + /** + * This command shall be used to set or update the pending Dataset of the Thread network to which the Border Router + * is connected, if the Border Router supports PAN Change. + * If the command is not executed via a CASE session, the command shall fail with a status code of + * UNSUPPORTED_ACCESS. + * This PendingDataset field shall contain the pending dataset to which the Thread network should be updated. The + * format of the data shall be an octet string containing the raw Thread TLV value of the pending dataset, as + * defined in the Thread specification. + * If any of the parameters in the PendingDataset is invalid, the command shall fail with a status of + * INVALID_COMMAND. + * Otherwise, this command shall configure the pending dataset of the Thread network to which the Border Router is + * connected, with the value given in the PendingDataset parameter. The Border Router will manage activation of the + * pending dataset as defined in the Thread specification. + */ + public static ClusterCommand setPendingDatasetRequest(OctetString pendingDataset) { + Map map = new LinkedHashMap<>(); + if (pendingDataset != null) { + map.put("pendingDataset", pendingDataset); + } + return new ClusterCommand("setPendingDatasetRequest", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "borderRouterName : " + borderRouterName + "\n"; + str += "borderAgentId : " + borderAgentId + "\n"; + str += "threadVersion : " + threadVersion + "\n"; + str += "interfaceEnabled : " + interfaceEnabled + "\n"; + str += "activeDatasetTimestamp : " + activeDatasetTimestamp + "\n"; + str += "pendingDatasetTimestamp : " + pendingDatasetTimestamp + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadNetworkDiagnosticsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadNetworkDiagnosticsCluster.java new file mode 100644 index 00000000000..391f73424a5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadNetworkDiagnosticsCluster.java @@ -0,0 +1,937 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ThreadNetworkDiagnostics + * + * @author Dan Cunningham - Initial contribution + */ +public class ThreadNetworkDiagnosticsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0035; + public static final String CLUSTER_NAME = "ThreadNetworkDiagnostics"; + public static final String CLUSTER_PREFIX = "threadNetworkDiagnostics"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_CHANNEL = "channel"; + public static final String ATTRIBUTE_ROUTING_ROLE = "routingRole"; + public static final String ATTRIBUTE_NETWORK_NAME = "networkName"; + public static final String ATTRIBUTE_PAN_ID = "panId"; + public static final String ATTRIBUTE_EXTENDED_PAN_ID = "extendedPanId"; + public static final String ATTRIBUTE_MESH_LOCAL_PREFIX = "meshLocalPrefix"; + public static final String ATTRIBUTE_OVERRUN_COUNT = "overrunCount"; + public static final String ATTRIBUTE_NEIGHBOR_TABLE = "neighborTable"; + public static final String ATTRIBUTE_ROUTE_TABLE = "routeTable"; + public static final String ATTRIBUTE_PARTITION_ID = "partitionId"; + public static final String ATTRIBUTE_WEIGHTING = "weighting"; + public static final String ATTRIBUTE_DATA_VERSION = "dataVersion"; + public static final String ATTRIBUTE_STABLE_DATA_VERSION = "stableDataVersion"; + public static final String ATTRIBUTE_LEADER_ROUTER_ID = "leaderRouterId"; + public static final String ATTRIBUTE_DETACHED_ROLE_COUNT = "detachedRoleCount"; + public static final String ATTRIBUTE_CHILD_ROLE_COUNT = "childRoleCount"; + public static final String ATTRIBUTE_ROUTER_ROLE_COUNT = "routerRoleCount"; + public static final String ATTRIBUTE_LEADER_ROLE_COUNT = "leaderRoleCount"; + public static final String ATTRIBUTE_ATTACH_ATTEMPT_COUNT = "attachAttemptCount"; + public static final String ATTRIBUTE_PARTITION_ID_CHANGE_COUNT = "partitionIdChangeCount"; + public static final String ATTRIBUTE_BETTER_PARTITION_ATTACH_ATTEMPT_COUNT = "betterPartitionAttachAttemptCount"; + public static final String ATTRIBUTE_PARENT_CHANGE_COUNT = "parentChangeCount"; + public static final String ATTRIBUTE_TX_TOTAL_COUNT = "txTotalCount"; + public static final String ATTRIBUTE_TX_UNICAST_COUNT = "txUnicastCount"; + public static final String ATTRIBUTE_TX_BROADCAST_COUNT = "txBroadcastCount"; + public static final String ATTRIBUTE_TX_ACK_REQUESTED_COUNT = "txAckRequestedCount"; + public static final String ATTRIBUTE_TX_ACKED_COUNT = "txAckedCount"; + public static final String ATTRIBUTE_TX_NO_ACK_REQUESTED_COUNT = "txNoAckRequestedCount"; + public static final String ATTRIBUTE_TX_DATA_COUNT = "txDataCount"; + public static final String ATTRIBUTE_TX_DATA_POLL_COUNT = "txDataPollCount"; + public static final String ATTRIBUTE_TX_BEACON_COUNT = "txBeaconCount"; + public static final String ATTRIBUTE_TX_BEACON_REQUEST_COUNT = "txBeaconRequestCount"; + public static final String ATTRIBUTE_TX_OTHER_COUNT = "txOtherCount"; + public static final String ATTRIBUTE_TX_RETRY_COUNT = "txRetryCount"; + public static final String ATTRIBUTE_TX_DIRECT_MAX_RETRY_EXPIRY_COUNT = "txDirectMaxRetryExpiryCount"; + public static final String ATTRIBUTE_TX_INDIRECT_MAX_RETRY_EXPIRY_COUNT = "txIndirectMaxRetryExpiryCount"; + public static final String ATTRIBUTE_TX_ERR_CCA_COUNT = "txErrCcaCount"; + public static final String ATTRIBUTE_TX_ERR_ABORT_COUNT = "txErrAbortCount"; + public static final String ATTRIBUTE_TX_ERR_BUSY_CHANNEL_COUNT = "txErrBusyChannelCount"; + public static final String ATTRIBUTE_RX_TOTAL_COUNT = "rxTotalCount"; + public static final String ATTRIBUTE_RX_UNICAST_COUNT = "rxUnicastCount"; + public static final String ATTRIBUTE_RX_BROADCAST_COUNT = "rxBroadcastCount"; + public static final String ATTRIBUTE_RX_DATA_COUNT = "rxDataCount"; + public static final String ATTRIBUTE_RX_DATA_POLL_COUNT = "rxDataPollCount"; + public static final String ATTRIBUTE_RX_BEACON_COUNT = "rxBeaconCount"; + public static final String ATTRIBUTE_RX_BEACON_REQUEST_COUNT = "rxBeaconRequestCount"; + public static final String ATTRIBUTE_RX_OTHER_COUNT = "rxOtherCount"; + public static final String ATTRIBUTE_RX_ADDRESS_FILTERED_COUNT = "rxAddressFilteredCount"; + public static final String ATTRIBUTE_RX_DEST_ADDR_FILTERED_COUNT = "rxDestAddrFilteredCount"; + public static final String ATTRIBUTE_RX_DUPLICATED_COUNT = "rxDuplicatedCount"; + public static final String ATTRIBUTE_RX_ERR_NO_FRAME_COUNT = "rxErrNoFrameCount"; + public static final String ATTRIBUTE_RX_ERR_UNKNOWN_NEIGHBOR_COUNT = "rxErrUnknownNeighborCount"; + public static final String ATTRIBUTE_RX_ERR_INVALID_SRC_ADDR_COUNT = "rxErrInvalidSrcAddrCount"; + public static final String ATTRIBUTE_RX_ERR_SEC_COUNT = "rxErrSecCount"; + public static final String ATTRIBUTE_RX_ERR_FCS_COUNT = "rxErrFcsCount"; + public static final String ATTRIBUTE_RX_ERR_OTHER_COUNT = "rxErrOtherCount"; + public static final String ATTRIBUTE_ACTIVE_TIMESTAMP = "activeTimestamp"; + public static final String ATTRIBUTE_PENDING_TIMESTAMP = "pendingTimestamp"; + public static final String ATTRIBUTE_DELAY = "delay"; + public static final String ATTRIBUTE_SECURITY_POLICY = "securityPolicy"; + public static final String ATTRIBUTE_CHANNEL_PAGE0MASK = "channelPage0Mask"; + public static final String ATTRIBUTE_OPERATIONAL_DATASET_COMPONENTS = "operationalDatasetComponents"; + public static final String ATTRIBUTE_ACTIVE_NETWORK_FAULTS_LIST = "activeNetworkFaultsList"; + public static final String ATTRIBUTE_EXT_ADDRESS = "extAddress"; + public static final String ATTRIBUTE_RLOC16 = "rloc16"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The Channel attribute shall indicate the 802.15.4 channel number configured on the Node’s Thread interface (that + * is, the Active Operational Dataset’s current Channel value). A value of null shall indicate that the Thread + * interface is not currently configured or operational. + */ + public Integer channel; // 0 uint16 R V + /** + * The RoutingRole attribute shall indicate the role that this Node has within the routing of messages through the + * Thread network, as defined by RoutingRoleEnum. The potential roles are defined in the following table. A value of + * null shall indicate that the Thread interface is not currently configured or operational. + */ + public RoutingRoleEnum routingRole; // 1 RoutingRoleEnum R V + /** + * The NetworkName attribute shall indicate a human-readable (displayable) name for the Thread network that the Node + * has been configured to join to. A value of null shall indicate that the Thread interface is not currently + * configured or operational. + */ + public String networkName; // 2 string R V + /** + * The PanId attribute shall indicate the 16-bit identifier of the Node on the Thread network. A value of null shall + * indicate that the Thread interface is not currently configured or operational. + */ + public Integer panId; // 3 uint16 R V + /** + * The ExtendedPanId attribute shall indicate the unique 64-bit identifier of the Node on the Thread network. A + * value of null shall indicate that the Thread interface is not currently configured or operational. + */ + public BigInteger extendedPanId; // 4 uint64 R V + /** + * The MeshLocalPrefix attribute shall indicate the mesh-local IPv6 prefix for the Thread network that the Node has + * been configured to join to. A value of null shall indicate that the Thread interface is not currently configured + * or operational. + */ + public OctetString meshLocalPrefix; // 5 ipv6pre R V + /** + * The OverrunCount attribute shall indicate the number of packets dropped either at ingress or egress, due to lack + * of buffer memory to retain all packets on the ethernet network interface. The OverrunCount attribute shall be + * reset to 0 upon a reboot of the Node. + */ + public BigInteger overrunCount; // 6 uint64 R V + /** + * The NeighborTable attribute shall indicate the current list of Nodes that comprise the neighbor table on the + * Node. + */ + public List neighborTable; // 7 list R V + /** + * The RouteTable attribute shall indicate the current list of router capable Nodes for which routes have been + * established. + */ + public List routeTable; // 8 list R V + /** + * The PartitionId attribute shall indicate the Thread Leader Partition Id for the Thread network to which the Node + * is joined. Null if not attached to a Thread network. + */ + public Integer partitionId; // 9 uint32 R V + /** + * The Weighting attribute shall indicate the Thread Leader Weight used when operating in the Leader role. Null if + * not attached to a Thread network. + */ + public Integer weighting; // 10 uint16 R V + /** + * The DataVersion attribute shall indicate the full Network Data Version the Node currently uses. Null if not + * attached to a Thread network. + */ + public Integer dataVersion; // 11 uint16 R V + /** + * The StableDataVersion attribute shall indicate the Network Data Version for the stable subset of data the Node + * currently uses. Null if not attached to a Thread network. + */ + public Integer stableDataVersion; // 12 uint16 R V + /** + * The LeaderRouterId attribute shall indicate the 8-bit LeaderRouterId the Node shall attempt to utilize upon + * becoming a router or leader on the Thread network. Null if not attached to a Thread network. + */ + public Integer leaderRouterId; // 13 uint8 R V + /** + * The DetachedRoleCount attribute shall indicate the number of times the Node entered the OT_DEVICE_ROLE_DETACHED + * role as specified within the Thread specification. This value shall only be reset upon a Node reboot. + */ + public Integer detachedRoleCount; // 14 uint16 R V + /** + * The ChildRoleCount attribute shall indicate the number of times the Node entered the OT_DEVICE_ROLE_CHILD role as + * specified within the Thread specification. This value shall only be reset upon a Node reboot. + */ + public Integer childRoleCount; // 15 uint16 R V + /** + * The RouterRoleCount attribute shall indicate the number of times the Node entered the OT_DEVICE_ROLE_ROUTER role + * as specified within the Thread specification. This value shall only be reset upon a Node reboot. + */ + public Integer routerRoleCount; // 16 uint16 R V + /** + * The LeaderRoleCount attribute shall indicate the number of times the Node entered the OT_DEVICE_ROLE_LEADER role + * as specified within the Thread specification. This value shall only be reset upon a Node reboot. + */ + public Integer leaderRoleCount; // 17 uint16 R V + /** + * The AttachAttemptCount attribute shall indicate the number of attempts that have been made to attach to a Thread + * network while the Node was detached from all Thread networks. This value shall only be reset upon a Node reboot. + */ + public Integer attachAttemptCount; // 18 uint16 R V + /** + * The PartitionIdChangeCount attribute shall indicate the number of times that the Thread network that the Node is + * connected to has changed its Partition ID. This value shall only be reset upon a Node reboot. + */ + public Integer partitionIdChangeCount; // 19 uint16 R V + /** + * The BetterPartitionAttachAttemptCount attribute shall indicate the number of times a Node has attempted to attach + * to a different Thread partition that it has determined is better than the partition it is currently attached to. + * This value shall only be reset upon a Node reboot. + */ + public Integer betterPartitionAttachAttemptCount; // 20 uint16 R V + /** + * The ParentChangeCount attribute shall indicate the number of times a Node has changed its parent. This value + * shall only be reset upon a Node reboot. + */ + public Integer parentChangeCount; // 21 uint16 R V + /** + * The TxTotalCount attribute shall indicate the total number of unique MAC frame transmission requests. The + * TxTotalCount attribute shall only be incremented by 1 for each MAC transmission request regardless of the amount + * of CCA failures, CSMA-CA attempts, or retransmissions. This value shall only be reset upon a Node reboot. + */ + public Integer txTotalCount; // 22 uint32 R V + /** + * The TxUnicastCount attribute shall indicate the total number of unique unicast MAC frame transmission requests. + * The TxUnicastCount attribute shall only be incremented by 1 for each unicast MAC transmission request regardless + * of the amount of CCA failures, CSMA-CA attempts, or retransmissions. This value shall only be reset upon a Node + * reboot. + */ + public Integer txUnicastCount; // 23 uint32 R V + /** + * The TxBroadcastCount attribute shall indicate the total number of unique broadcast MAC frame transmission + * requests. The TxBroadcastCount attribute shall only be incremented by 1 for each broadcast MAC transmission + * request regardless of the amount of CCA failures, CSMA-CA attempts, or retransmissions. This value shall only be + * reset upon a Node reboot. + */ + public Integer txBroadcastCount; // 24 uint32 R V + /** + * The TxAckRequestedCount attribute shall indicate the total number of unique MAC frame transmission requests with + * requested acknowledgment. The TxAckRequestedCount attribute shall only be incremented by 1 for each MAC + * transmission request with requested acknowledgment regardless of the amount of CCA failures, CSMA-CA attempts, or + * retransmissions. This value shall only be reset upon a Node reboot. + */ + public Integer txAckRequestedCount; // 25 uint32 R V + /** + * The TxAckedCount attribute shall indicate the total number of unique MAC frame transmission requests that were + * acked. The TxAckedCount attribute shall only be incremented by 1 for each MAC transmission request that is acked + * regardless of the amount of CCA failures, CSMA-CA attempts, or retransmissions. This value shall only be reset + * upon a Node reboot. + */ + public Integer txAckedCount; // 26 uint32 R V + /** + * The TxNoAckRequestedCount attribute shall indicate the total number of unique MAC frame transmission requests + * without requested acknowledgment. The TxNoAckRequestedCount attribute shall only be incremented by 1 for each MAC + * transmission request that is does not request acknowledgement regardless of the amount of CCA failures, CSMA-CA + * attempts, or retransmissions. + */ + public Integer txNoAckRequestedCount; // 27 uint32 R V + /** + * The TxDataCount attribute shall indicate the total number of unique MAC Data frame transmission requests. The + * TxDataCount attribute shall only be incremented by 1 for each MAC Data frame transmission request regardless of + * the amount of CCA failures, CSMA-CA attempts, or retransmissions. This value shall only be reset upon a Node + * reboot. + */ + public Integer txDataCount; // 28 uint32 R V + /** + * The TxDataPollCount attribute shall indicate the total number of unique MAC Data Poll frame transmission + * requests. The TxDataPollCount attribute shall only be incremented by 1 for each MAC Data Poll frame transmission + * request regardless of the amount of CCA failures, CSMA-CA attempts, or retransmissions. This value shall only be + * reset upon a Node reboot. + */ + public Integer txDataPollCount; // 29 uint32 R V + /** + * The TxBeaconCount attribute shall indicate the total number of unique MAC Beacon frame transmission requests. The + * TxBeaconCount attribute shall only be incremented by 1 for each MAC Beacon frame transmission request regardless + * of the amount of CCA failures, CSMA-CA attempts, or retransmissions. + */ + public Integer txBeaconCount; // 30 uint32 R V + /** + * The TxBeaconRequestCount attribute shall indicate the total number of unique MAC Beacon Request frame + * transmission requests. The TxBeaconRequestCount attribute shall only be incremented by 1 for each MAC Beacon + * Request frame transmission request regardless of the amount of CCA failures, CSMA-CA attempts, or + * retransmissions. This value shall only be reset upon a Node reboot. + */ + public Integer txBeaconRequestCount; // 31 uint32 R V + /** + * The TxOtherCount attribute shall indicate the total number of unique MAC frame transmission requests that are not + * counted by any other attribute. The TxOtherCount attribute shall only be incremented by 1 for each MAC frame + * transmission request regardless of the amount of CCA failures, CSMA-CA attempts, or retransmissions. This value + * shall only be reset upon a Node reboot. + */ + public Integer txOtherCount; // 32 uint32 R V + /** + * The TxRetryCount attribute shall indicate the total number of MAC retransmission attempts. The TxRetryCount + * attribute shall only be incremented by 1 for each retransmission attempt that may be triggered by lack of + * acknowledgement, CSMA/CA failure, or other type of transmission error. This value shall only be reset upon a Node + * reboot. + */ + public Integer txRetryCount; // 33 uint32 R V + /** + * The TxDirectMaxRetryExpiryCount attribute shall indicate the total number of unique MAC transmission packets that + * meet maximal retry limit for direct packets. The TxDirectMaxRetryExpiryCount attribute shall only be incremented + * by 1 for each unique MAC transmission packets that meets the maximal retry limit for direct packets. This value + * shall only be reset upon a Node reboot. + */ + public Integer txDirectMaxRetryExpiryCount; // 34 uint32 R V + /** + * The TxIndirectMaxRetryExpiryCount attribute shall indicate the total number of unique MAC transmission packets + * that meet maximal retry limit for indirect packets. The TxIndirectMaxRetryExpiryCount attribute shall only be + * incremented by 1 for each unique MAC transmission packets that meets the maximal retry limit for indirect + * packets. This value shall only be reset upon a Node reboot. + */ + public Integer txIndirectMaxRetryExpiryCount; // 35 uint32 R V + /** + * The TxErrCcaCount attribute shall indicate the total number of CCA failures. The TxErrCcaCount attribute shall + * only be incremented by 1 for each instance of a CCA failure. This value shall only be reset upon a Node reboot. + */ + public Integer txErrCcaCount; // 36 uint32 R V + /** + * The TxErrAbortCount attribute shall indicate the total number of unique MAC transmission request failures caused + * by an abort error. The TxErrAbortCount attribute shall only be incremented by 1 for each unique MAC transmission + * request failure caused by an abort error. + */ + public Integer txErrAbortCount; // 37 uint32 R V + /** + * The TxErrBusyChannelCount attribute shall indicate the total number of unique MAC transmission request failures + * caused by an error as the result of a busy channel (a CSMA/CA fail). The TxErrBusyChannelCount attribute shall + * only be incremented by 1 for each unique MAC transmission request failure caused by a busy channel such as a + * CSMA/CA failure. + */ + public Integer txErrBusyChannelCount; // 38 uint32 R V + /** + * The RxTotalCount attribute shall indicate the total number of received unique MAC frames. This value shall only + * be reset upon a Node reboot. + */ + public Integer rxTotalCount; // 39 uint32 R V + /** + * The RxUnicastCount attribute shall indicate the total number of received unique unicast MAC frames. This value + * shall only be reset upon a Node reboot. + */ + public Integer rxUnicastCount; // 40 uint32 R V + /** + * The RxBroadcastCount attribute shall indicate the total number of received unique broadcast MAC frames. This + * value shall only be reset upon a Node reboot. + */ + public Integer rxBroadcastCount; // 41 uint32 R V + /** + * The RxDataCount attribute shall indicate the total number of received unique MAC Data frames. This value shall + * only be reset upon a Node reboot. + */ + public Integer rxDataCount; // 42 uint32 R V + /** + * The RxDataPollCount attribute shall indicate the total number of received unique MAC Data Poll frames. This value + * shall only be reset upon a Node reboot. + */ + public Integer rxDataPollCount; // 43 uint32 R V + /** + * The RxBeaconCount attribute shall indicate the total number of received unique MAC Beacon frames. This value + * shall only be reset upon a Node reboot. + */ + public Integer rxBeaconCount; // 44 uint32 R V + /** + * The RxBeaconRequestCount attribute shall indicate the total number of received unique MAC Beacon Request frames. + * This value shall only be reset upon a Node reboot. + */ + public Integer rxBeaconRequestCount; // 45 uint32 R V + /** + * The RxOtherCount attribute shall indicate the total number of received unique MAC frame requests that are not + * counted by any other attribute. This value shall only be reset upon a Node reboot. + */ + public Integer rxOtherCount; // 46 uint32 R V + /** + * The RxAddressFilteredCount attribute shall indicate the total number of received unique MAC frame requests that + * have been dropped as a result of MAC filtering. This value shall only be reset upon a Node reboot. + */ + public Integer rxAddressFilteredCount; // 47 uint32 R V + /** + * The RxDestAddrFilteredCount attribute shall indicate the total number of received unique MAC frame requests that + * have been dropped as a result of a destination address check. This value shall only be reset upon a Node reboot. + */ + public Integer rxDestAddrFilteredCount; // 48 uint32 R V + /** + * The RxDuplicatedCount attribute shall indicate the total number of received MAC frame requests that have been + * dropped as a result of being a duplicate of a previously received MAC frame request. This value shall only be + * reset upon a Node reboot. + */ + public Integer rxDuplicatedCount; // 49 uint32 R V + /** + * The RxErrNoFrameCount attribute shall indicate the total number of received unique MAC frame requests that have + * been dropped as a result of missing or malformed frame contents. This value shall only be reset upon a Node + * reboot. + */ + public Integer rxErrNoFrameCount; // 50 uint32 R V + /** + * The RxErrUnknownNeighborCount attribute shall indicate the total number of received unique MAC frame requests + * that have been dropped as a result of originating from an unknown neighbor device. This value shall only be reset + * upon a Node reboot. + */ + public Integer rxErrUnknownNeighborCount; // 51 uint32 R V + /** + * The RxErrInvalidSrcAddrCount attribute shall indicate the total number of received unique MAC frame requests that + * have been dropped as a result of containing an invalid source address. This value shall only be reset upon a Node + * reboot. + */ + public Integer rxErrInvalidSrcAddrCount; // 52 uint32 R V + /** + * The RxErrSecCount attribute shall indicate the total number of received unique MAC frame requests that have been + * dropped as a result of an error with the security of the received frame. This value shall only be reset upon a + * Node reboot. + */ + public Integer rxErrSecCount; // 53 uint32 R V + /** + * The RxErrFcsCount attribute shall indicate the total number of received unique MAC frame requests that have been + * dropped as a result of an error with the FCS of the received frame. This value shall only be reset upon a Node + * reboot. + */ + public Integer rxErrFcsCount; // 54 uint32 R V + /** + * The RxErrOtherCount attribute shall indicate the total number of received unique MAC frame requests that have + * been dropped as a result of an error that is not counted by any other attribute. This value shall only be reset + * upon a Node reboot. + */ + public Integer rxErrOtherCount; // 55 uint32 R V + /** + * Null when there is no dataset configured. + */ + public BigInteger activeTimestamp; // 56 uint64 R V + /** + * Null when there is no dataset configured. + */ + public BigInteger pendingTimestamp; // 57 uint64 R V + /** + * Null when there is no dataset configured. + */ + public Integer delay; // 58 uint32 R V + /** + * The SecurityPolicy attribute indicates the current security policies for the Thread partition to which a Node is + * connected. Null when there is no dataset configured. + */ + public SecurityPolicy securityPolicy; // 59 SecurityPolicy R V + /** + * The ChannelPage0Mask attribute indicates the channels within channel page 0, in the 2.4GHz ISM band. The channels + * are represented in most significant bit order, with bit value 1 meaning selected, bit value 0 meaning unselected. + * For example, the most significant bit of the left-most byte indicates channel 0. If channel 0 and channel 10 are + * selected, the mask would be: 80 20 00 00. Null when there is no dataset configured. + */ + public OctetString channelPage0Mask; // 60 octstr R V + /** + * The OperationalDatasetComponents attribute is a collection of flags to indicate the presence of various + * operationally acquired values. + */ + public OperationalDatasetComponents operationalDatasetComponents; // 61 OperationalDatasetComponents R V + public List activeNetworkFaultsList; // 62 list R V + public BigInteger extAddress; // 63 uint64 R V + public Integer rloc16; // 64 uint16 R V + // Structs + + /** + * The ConnectionStatus Event shall indicate that a Node’s connection status to a Thread network has changed. + */ + public class ConnectionStatus { + public ConnectionStatusEnum connectionStatus; // ConnectionStatusEnum + + public ConnectionStatus(ConnectionStatusEnum connectionStatus) { + this.connectionStatus = connectionStatus; + } + } + + /** + * The NetworkFaultChange Event shall indicate a change in the set of network faults currently detected by the Node. + */ + public class NetworkFaultChange { + /** + * This field shall represent the set of faults currently detected, as per Section 11.14.5.1, “NetworkFaultEnum + * Type”. + */ + public List current; // list + /** + * This field shall represent the set of faults detected prior to this change event, as per Section 11.14.5.1, + * “NetworkFaultEnum Type”. + */ + public List previous; // list + + public NetworkFaultChange(List current, List previous) { + this.current = current; + this.previous = previous; + } + } + + public class NeighborTableStruct { + /** + * This field shall specify the IEEE 802.15.4 extended address for the neighboring Node. + */ + public BigInteger extAddress; // uint64 + /** + * This field shall specify the duration of time, in seconds, since a frame has been received from the + * neighboring Node. + */ + public Integer age; // uint32 + /** + * This field shall specify the RLOC16 of the neighboring Node. + */ + public Integer rloc16; // uint16 + /** + * This field shall specify the number of link layer frames that have been received from the neighboring node. + * This field shall be reset to 0 upon a reboot of the Node. + */ + public Integer linkFrameCounter; // uint32 + /** + * This field shall specify the number of Mesh Link Establishment frames that have been received from the + * neighboring node. This field shall be reset to 0 upon a reboot of the Node. + */ + public Integer mleFrameCounter; // uint32 + /** + * This field shall specify the implementation specific mix of IEEE 802.15.4 PDU receive quality indicators, + * scaled from 0 to 255. + */ + public Integer lqi; // uint8 + /** + * This field SHOULD specify the average RSSI across all received frames from the neighboring Node since the + * receiving Node’s last reboot. If there is no known received frames this field SHOULD have the value of null. + * This field shall have the units of dBm, having the range -128 dBm to 0 dBm. + */ + public Integer averageRssi; // int8 + /** + * This field shall specify the RSSI of the most recently received frame from the neighboring Node. If there is + * no known last received frame the LastRssi field SHOULD have the value of null. This field shall have the + * units of dBm, having the range -128 dBm to 0 dBm. + */ + public Integer lastRssi; // int8 + /** + * This field shall specify the percentage of received frames from the neighboring Node that have resulted in + * errors. + */ + public Integer frameErrorRate; // uint8 + /** + * This field shall specify the percentage of received messages from the neighboring Node that have resulted in + * errors. + */ + public Integer messageErrorRate; // uint8 + /** + * This field shall specify if the neighboring Node is capable of receiving frames while the Node is in an idle + * state. + */ + public Boolean rxOnWhenIdle; // bool + /** + * This field shall specify if the neighboring Node is a full Thread device. + */ + public Boolean fullThreadDevice; // bool + /** + * This field shall specify if the neighboring Node requires the full Network Data. If set to False, the + * neighboring Node only requires the stable Network Data. + */ + public Boolean fullNetworkData; // bool + /** + * This field shall specify if the neighboring Node is a direct child of the Node reporting the NeighborTable + * attribute. + */ + public Boolean isChild; // bool + + public NeighborTableStruct(BigInteger extAddress, Integer age, Integer rloc16, Integer linkFrameCounter, + Integer mleFrameCounter, Integer lqi, Integer averageRssi, Integer lastRssi, Integer frameErrorRate, + Integer messageErrorRate, Boolean rxOnWhenIdle, Boolean fullThreadDevice, Boolean fullNetworkData, + Boolean isChild) { + this.extAddress = extAddress; + this.age = age; + this.rloc16 = rloc16; + this.linkFrameCounter = linkFrameCounter; + this.mleFrameCounter = mleFrameCounter; + this.lqi = lqi; + this.averageRssi = averageRssi; + this.lastRssi = lastRssi; + this.frameErrorRate = frameErrorRate; + this.messageErrorRate = messageErrorRate; + this.rxOnWhenIdle = rxOnWhenIdle; + this.fullThreadDevice = fullThreadDevice; + this.fullNetworkData = fullNetworkData; + this.isChild = isChild; + } + } + + public class RouteTableStruct { + /** + * This field shall specify the IEEE 802.15.4 extended address for the Node for which this route table entry + * corresponds. + */ + public BigInteger extAddress; // uint64 + /** + * This field shall specify the RLOC16 for the Node for which this route table entry corresponds. + */ + public Integer rloc16; // uint16 + /** + * This field shall specify the Router ID for the Node for which this route table entry corresponds. + */ + public Integer routerId; // uint8 + /** + * This field shall specify the Router ID for the next hop in the route to the Node for which this route table + * entry corresponds. + */ + public Integer nextHop; // uint8 + /** + * This Field shall specify the cost of the route to the Node for which this route table entry corresponds. + */ + public Integer pathCost; // uint8 + /** + * This field shall specify the implementation specific mix of IEEE 802.15.4 PDU receive quality indicators, + * scaled from 0 to 255, from the perspective of the Node reporting the neighbor table. + */ + public Integer lqiIn; // uint8 + /** + * This field shall specify the implementation specific mix of IEEE 802.15.4 PDU receive quality indicators, + * scaled from 0 to 255, from the perspective of the Node specified within the NextHop field. + */ + public Integer lqiOut; // uint8 + /** + * This field shall specify the duration of time, in seconds, since a frame has been received from the Node for + * which this route table entry corresponds. + */ + public Integer age; // uint8 + /** + * This field shall specify if the router ID as defined within the RouterId field has been allocated. + */ + public Boolean allocated; // bool + /** + * This field shall specify if a link has been established to the Node for which this route table entry + * corresponds. + */ + public Boolean linkEstablished; // bool + + public RouteTableStruct(BigInteger extAddress, Integer rloc16, Integer routerId, Integer nextHop, + Integer pathCost, Integer lqiIn, Integer lqiOut, Integer age, Boolean allocated, + Boolean linkEstablished) { + this.extAddress = extAddress; + this.rloc16 = rloc16; + this.routerId = routerId; + this.nextHop = nextHop; + this.pathCost = pathCost; + this.lqiIn = lqiIn; + this.lqiOut = lqiOut; + this.age = age; + this.allocated = allocated; + this.linkEstablished = linkEstablished; + } + } + + public class SecurityPolicy { + /** + * This field shall specify the interval of time, in hours, that Thread security keys are rotated. Null when + * there is no dataset configured. + */ + public Integer rotationTime; // uint16 + /** + * This field shall specify the flags as specified in Thread 1.3.0 section 8.10.1.15. Null when there is no + * dataset configured. + */ + public Integer flags; // uint16 + + public SecurityPolicy(Integer rotationTime, Integer flags) { + this.rotationTime = rotationTime; + this.flags = flags; + } + } + + public class OperationalDatasetComponents { + /** + * This field shall be True if the Node has an active timestamp present, else False. + */ + public Boolean activeTimestampPresent; // bool + /** + * This field shall be True if the Node has a pending timestamp is present, else False. + */ + public Boolean pendingTimestampPresent; // bool + /** + * This field shall be True if the Node has the Thread master key, else False. + */ + public Boolean masterKeyPresent; // bool + /** + * This field shall be True if the Node has the Thread network’s name, else False. + */ + public Boolean networkNamePresent; // bool + /** + * This field shall be True if the Node has an extended Pan ID, else False. + */ + public Boolean extendedPanIdPresent; // bool + /** + * This field shall be True if the Node has the mesh local prefix, else False. + */ + public Boolean meshLocalPrefixPresent; // bool + /** + * This field shall be True if the Node has the Thread network delay set, else False. + */ + public Boolean delayPresent; // bool + /** + * This field shall be True if the Node has a Pan ID, else False. + */ + public Boolean panIdPresent; // bool + /** + * This field shall be True if the Node has configured an operational channel for the Thread network, else + * False. + */ + public Boolean channelPresent; // bool + /** + * This field shall be True if the Node has been configured with the Thread network Pskc, else False. + */ + public Boolean pskcPresent; // bool + /** + * This field shall be True if the Node has been configured with the Thread network security policies, else + * False. + */ + public Boolean securityPolicyPresent; // bool + /** + * This field shall be True if the Node has available a mask of available channels, else False. + */ + public Boolean channelMaskPresent; // bool + + public OperationalDatasetComponents(Boolean activeTimestampPresent, Boolean pendingTimestampPresent, + Boolean masterKeyPresent, Boolean networkNamePresent, Boolean extendedPanIdPresent, + Boolean meshLocalPrefixPresent, Boolean delayPresent, Boolean panIdPresent, Boolean channelPresent, + Boolean pskcPresent, Boolean securityPolicyPresent, Boolean channelMaskPresent) { + this.activeTimestampPresent = activeTimestampPresent; + this.pendingTimestampPresent = pendingTimestampPresent; + this.masterKeyPresent = masterKeyPresent; + this.networkNamePresent = networkNamePresent; + this.extendedPanIdPresent = extendedPanIdPresent; + this.meshLocalPrefixPresent = meshLocalPrefixPresent; + this.delayPresent = delayPresent; + this.panIdPresent = panIdPresent; + this.channelPresent = channelPresent; + this.pskcPresent = pskcPresent; + this.securityPolicyPresent = securityPolicyPresent; + this.channelMaskPresent = channelMaskPresent; + } + } + + // Enums + public enum NetworkFaultEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + LINK_DOWN(1, "Link Down"), + HARDWARE_FAILURE(2, "Hardware Failure"), + NETWORK_JAMMED(3, "Network Jammed"); + + public final Integer value; + public final String label; + + private NetworkFaultEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ConnectionStatusEnum implements MatterEnum { + CONNECTED(0, "Connected"), + NOT_CONNECTED(1, "Not Connected"); + + public final Integer value; + public final String label; + + private ConnectionStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum RoutingRoleEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + UNASSIGNED(1, "Unassigned"), + SLEEPY_END_DEVICE(2, "Sleepy End Device"), + END_DEVICE(3, "End Device"), + REED(4, "Reed"), + ROUTER(5, "Router"), + LEADER(6, "Leader"); + + public final Integer value; + public final String label; + + private RoutingRoleEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Server supports the counts for the number of received and transmitted packets on the Thread interface. + */ + public boolean packetCounts; + /** + * + * Server supports the counts for the number of errors that have occurred during the reception and transmission + * of packets on the Thread interface. + */ + public boolean errorCounts; + /** + * + * Server supports the counts for various MLE layer happenings. + */ + public boolean mleCounts; + /** + * + * Server supports the counts for various MAC layer happenings. + */ + public boolean macCounts; + + public FeatureMap(boolean packetCounts, boolean errorCounts, boolean mleCounts, boolean macCounts) { + this.packetCounts = packetCounts; + this.errorCounts = errorCounts; + this.mleCounts = mleCounts; + this.macCounts = macCounts; + } + } + + public ThreadNetworkDiagnosticsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 53, "ThreadNetworkDiagnostics"); + } + + protected ThreadNetworkDiagnosticsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Reception of this command shall reset the following attributes to 0: + * • OverrunCount + * This command has no associated data. Upon completion, this command shall send a status code set to a value of + * SUCCESS back to the initiator. + */ + public static ClusterCommand resetCounts() { + return new ClusterCommand("resetCounts"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "channel : " + channel + "\n"; + str += "routingRole : " + routingRole + "\n"; + str += "networkName : " + networkName + "\n"; + str += "panId : " + panId + "\n"; + str += "extendedPanId : " + extendedPanId + "\n"; + str += "meshLocalPrefix : " + meshLocalPrefix + "\n"; + str += "overrunCount : " + overrunCount + "\n"; + str += "neighborTable : " + neighborTable + "\n"; + str += "routeTable : " + routeTable + "\n"; + str += "partitionId : " + partitionId + "\n"; + str += "weighting : " + weighting + "\n"; + str += "dataVersion : " + dataVersion + "\n"; + str += "stableDataVersion : " + stableDataVersion + "\n"; + str += "leaderRouterId : " + leaderRouterId + "\n"; + str += "detachedRoleCount : " + detachedRoleCount + "\n"; + str += "childRoleCount : " + childRoleCount + "\n"; + str += "routerRoleCount : " + routerRoleCount + "\n"; + str += "leaderRoleCount : " + leaderRoleCount + "\n"; + str += "attachAttemptCount : " + attachAttemptCount + "\n"; + str += "partitionIdChangeCount : " + partitionIdChangeCount + "\n"; + str += "betterPartitionAttachAttemptCount : " + betterPartitionAttachAttemptCount + "\n"; + str += "parentChangeCount : " + parentChangeCount + "\n"; + str += "txTotalCount : " + txTotalCount + "\n"; + str += "txUnicastCount : " + txUnicastCount + "\n"; + str += "txBroadcastCount : " + txBroadcastCount + "\n"; + str += "txAckRequestedCount : " + txAckRequestedCount + "\n"; + str += "txAckedCount : " + txAckedCount + "\n"; + str += "txNoAckRequestedCount : " + txNoAckRequestedCount + "\n"; + str += "txDataCount : " + txDataCount + "\n"; + str += "txDataPollCount : " + txDataPollCount + "\n"; + str += "txBeaconCount : " + txBeaconCount + "\n"; + str += "txBeaconRequestCount : " + txBeaconRequestCount + "\n"; + str += "txOtherCount : " + txOtherCount + "\n"; + str += "txRetryCount : " + txRetryCount + "\n"; + str += "txDirectMaxRetryExpiryCount : " + txDirectMaxRetryExpiryCount + "\n"; + str += "txIndirectMaxRetryExpiryCount : " + txIndirectMaxRetryExpiryCount + "\n"; + str += "txErrCcaCount : " + txErrCcaCount + "\n"; + str += "txErrAbortCount : " + txErrAbortCount + "\n"; + str += "txErrBusyChannelCount : " + txErrBusyChannelCount + "\n"; + str += "rxTotalCount : " + rxTotalCount + "\n"; + str += "rxUnicastCount : " + rxUnicastCount + "\n"; + str += "rxBroadcastCount : " + rxBroadcastCount + "\n"; + str += "rxDataCount : " + rxDataCount + "\n"; + str += "rxDataPollCount : " + rxDataPollCount + "\n"; + str += "rxBeaconCount : " + rxBeaconCount + "\n"; + str += "rxBeaconRequestCount : " + rxBeaconRequestCount + "\n"; + str += "rxOtherCount : " + rxOtherCount + "\n"; + str += "rxAddressFilteredCount : " + rxAddressFilteredCount + "\n"; + str += "rxDestAddrFilteredCount : " + rxDestAddrFilteredCount + "\n"; + str += "rxDuplicatedCount : " + rxDuplicatedCount + "\n"; + str += "rxErrNoFrameCount : " + rxErrNoFrameCount + "\n"; + str += "rxErrUnknownNeighborCount : " + rxErrUnknownNeighborCount + "\n"; + str += "rxErrInvalidSrcAddrCount : " + rxErrInvalidSrcAddrCount + "\n"; + str += "rxErrSecCount : " + rxErrSecCount + "\n"; + str += "rxErrFcsCount : " + rxErrFcsCount + "\n"; + str += "rxErrOtherCount : " + rxErrOtherCount + "\n"; + str += "activeTimestamp : " + activeTimestamp + "\n"; + str += "pendingTimestamp : " + pendingTimestamp + "\n"; + str += "delay : " + delay + "\n"; + str += "securityPolicy : " + securityPolicy + "\n"; + str += "channelPage0Mask : " + channelPage0Mask + "\n"; + str += "operationalDatasetComponents : " + operationalDatasetComponents + "\n"; + str += "activeNetworkFaultsList : " + activeNetworkFaultsList + "\n"; + str += "extAddress : " + extAddress + "\n"; + str += "rloc16 : " + rloc16 + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadNetworkDirectoryCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadNetworkDirectoryCluster.java new file mode 100644 index 00000000000..4bc94c50a99 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ThreadNetworkDirectoryCluster.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ThreadNetworkDirectory + * + * @author Dan Cunningham - Initial contribution + */ +public class ThreadNetworkDirectoryCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0453; + public static final String CLUSTER_NAME = "ThreadNetworkDirectory"; + public static final String CLUSTER_PREFIX = "threadNetworkDirectory"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_PREFERRED_EXTENDED_PAN_ID = "preferredExtendedPanId"; + public static final String ATTRIBUTE_THREAD_NETWORKS = "threadNetworks"; + public static final String ATTRIBUTE_THREAD_NETWORK_TABLE_SIZE = "threadNetworkTableSize"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the Thread Extended PAN ID value for the Thread network designated by the user to be their preferred + * network for commissioning of Thread devices. If not null, the value of this attribute shall match the + * ExtendedPanID of a network in the ThreadNetworks attribute. A write operation with a non-null value that does not + * match any network in the ThreadNetworks list shall be rejected with a status of CONSTRAINT_ERROR. + * The purpose of designating one Thread network as preferred is to help a commissioner to select a Thread network + * when a Thread device is within suitable range of more than one Thread network which appears in the ThreadNetworks + * list. A value of null indicates that there is no current preferred network: All networks may be treated as + * equally preferred by a commissioner with access to this cluster. + * This attribute may be automatically set to the ExtendedPanID of the first Thread network added to the + * ThreadNetworks list. + * A client shall obtain user consent before changing the value of this attribute from a non-null value. + * On a factory reset this attribute shall be reset to null. + */ + public OctetString preferredExtendedPanId; // 0 octstr RW VM + /** + * Indicates the list of Thread Networks known about by this cluster. If the node hosting this cluster includes a + * Thread Border Router, then an entry for its Thread Network shall be included in this list. + * The list can be modified via the AddNetwork and RemoveNetwork commands. + * For each entry in the list, the cluster server also stores a Thread Operational Dataset. Clients use the + * GetOperationalDataset command to obtain the Operational Dataset for an entry in this list. + * On a factory reset this list shall be cleared, and any Thread Operational datasets previously stored shall be + * removed from the Node. + */ + public List threadNetworks; // 1 list R V + /** + * This attribute shall indicate the maximum number of entries that can be held in the ThreadNetworks list; it shall + * be at least 2 times the number of SupportedFabrics advertised in the Operational Credentials Cluster on the root + * endpoint of this node. + */ + public Integer threadNetworkTableSize; // 2 uint8 R V + // Structs + + /** + * Represents the data associated with a Thread Network. + */ + public class ThreadNetworkStruct { + /** + * This field shall indicate the Extended PAN ID from the OperationalDataset for the given Thread network. + */ + public OctetString extendedPanId; // octstr + /** + * This field shall indicate the Network Name from the OperationalDataset for the given Thread network. + */ + public String networkName; // string + /** + * This field shall indicate the Channel from the OperationalDataset for the given Thread network. + */ + public Integer channel; // uint16 + /** + * This field shall indicate the Active Timestamp from the OperationalDataset for the given Thread network. + */ + public BigInteger activeTimestamp; // uint64 + + public ThreadNetworkStruct(OctetString extendedPanId, String networkName, Integer channel, + BigInteger activeTimestamp) { + this.extendedPanId = extendedPanId; + this.networkName = networkName; + this.channel = channel; + this.activeTimestamp = activeTimestamp; + } + } + + public ThreadNetworkDirectoryCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1107, "ThreadNetworkDirectory"); + } + + protected ThreadNetworkDirectoryCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Adds an entry to the ThreadNetworks attribute with the specified Thread Operational Dataset. + * If there is an existing entry with the Extended PAN ID then the Thread Operational Dataset for that entry is + * replaced. As a result, changes to the network parameters (e.g. Channel, Network Name, PSKc, …) of an existing + * entry with a given Extended PAN ID can be made using this command. + */ + public static ClusterCommand addNetwork(OctetString operationalDataset) { + Map map = new LinkedHashMap<>(); + if (operationalDataset != null) { + map.put("operationalDataset", operationalDataset); + } + return new ClusterCommand("addNetwork", map); + } + + /** + * Removes the network with the given Extended PAN ID from the ThreadNetworks attribute. + */ + public static ClusterCommand removeNetwork(OctetString extendedPanId) { + Map map = new LinkedHashMap<>(); + if (extendedPanId != null) { + map.put("extendedPanId", extendedPanId); + } + return new ClusterCommand("removeNetwork", map); + } + + /** + * Retrieves the Thread Operational Dataset with the given Extended PAN ID. + */ + public static ClusterCommand getOperationalDataset(OctetString extendedPanId) { + Map map = new LinkedHashMap<>(); + if (extendedPanId != null) { + map.put("extendedPanId", extendedPanId); + } + return new ClusterCommand("getOperationalDataset", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "preferredExtendedPanId : " + preferredExtendedPanId + "\n"; + str += "threadNetworks : " + threadNetworks + "\n"; + str += "threadNetworkTableSize : " + threadNetworkTableSize + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TimeFormatLocalizationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TimeFormatLocalizationCluster.java new file mode 100644 index 00000000000..3aa4193ef3f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TimeFormatLocalizationCluster.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * TimeFormatLocalization + * + * @author Dan Cunningham - Initial contribution + */ +public class TimeFormatLocalizationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x002C; + public static final String CLUSTER_NAME = "TimeFormatLocalization"; + public static final String CLUSTER_PREFIX = "timeFormatLocalization"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_HOUR_FORMAT = "hourFormat"; + public static final String ATTRIBUTE_ACTIVE_CALENDAR_TYPE = "activeCalendarType"; + public static final String ATTRIBUTE_SUPPORTED_CALENDAR_TYPES = "supportedCalendarTypes"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the format that the Node is currently configured to use when conveying the hour unit of time. + * If not UseActiveLocale, this value shall take priority over any unit implied through the ActiveLocale attribute. + * If UseActiveLocale, any unit implied through the ActiveLocale attribute is used as the hour format, and if + * ActiveLocale is not present, the hour format is unknown. + */ + public HourFormatEnum hourFormat; // 0 HourFormatEnum RW VM + /** + * Indicates the calendar format that the Node is currently configured to use when conveying dates. + * If not UseActiveLocale, this value shall take priority over any unit implied through the ActiveLocale attribute. + * If UseActiveLocale, any unit implied through the ActiveLocale attribute is used as the calendar type, and if + * ActiveLocale is not present, the calendar type is unknown. + */ + public CalendarTypeEnum activeCalendarType; // 1 CalendarTypeEnum RW VM + /** + * Indicates a list of CalendarTypeEnum values that are supported by the Node. The list shall NOT contain any + * duplicate entries. The ordering of items within the list SHOULD NOT express any meaning. The maximum length of + * the SupportedCalendarTypes list shall be equivalent to the number of enumerations within CalendarTypeEnum. + */ + public List supportedCalendarTypes; // 2 list R V + + // Enums + public enum HourFormatEnum implements MatterEnum { + V12HR(0, "12 Hr"), + V24HR(1, "24 Hr"), + USE_ACTIVE_LOCALE(255, "Use Active Locale"); + + public final Integer value; + public final String label; + + private HourFormatEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum CalendarTypeEnum implements MatterEnum { + BUDDHIST(0, "Buddhist"), + CHINESE(1, "Chinese"), + COPTIC(2, "Coptic"), + ETHIOPIAN(3, "Ethiopian"), + GREGORIAN(4, "Gregorian"), + HEBREW(5, "Hebrew"), + INDIAN(6, "Indian"), + ISLAMIC(7, "Islamic"), + JAPANESE(8, "Japanese"), + KOREAN(9, "Korean"), + PERSIAN(10, "Persian"), + TAIWANESE(11, "Taiwanese"), + USE_ACTIVE_LOCALE(255, "Use Active Locale"); + + public final Integer value; + public final String label; + + private CalendarTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * The Node can be configured to use different calendar formats when conveying values to a user. + */ + public boolean calendarFormat; + + public FeatureMap(boolean calendarFormat) { + this.calendarFormat = calendarFormat; + } + } + + public TimeFormatLocalizationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 44, "TimeFormatLocalization"); + } + + protected TimeFormatLocalizationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "hourFormat : " + hourFormat + "\n"; + str += "activeCalendarType : " + activeCalendarType + "\n"; + str += "supportedCalendarTypes : " + supportedCalendarTypes + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TimeSynchronizationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TimeSynchronizationCluster.java new file mode 100644 index 00000000000..ecdaa9e57d1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TimeSynchronizationCluster.java @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * TimeSynchronization + * + * @author Dan Cunningham - Initial contribution + */ +public class TimeSynchronizationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0038; + public static final String CLUSTER_NAME = "TimeSynchronization"; + public static final String CLUSTER_PREFIX = "timeSynchronization"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_UTC_TIME = "utcTime"; + public static final String ATTRIBUTE_GRANULARITY = "granularity"; + public static final String ATTRIBUTE_TIME_SOURCE = "timeSource"; + public static final String ATTRIBUTE_TRUSTED_TIME_SOURCE = "trustedTimeSource"; + public static final String ATTRIBUTE_DEFAULT_NTP = "defaultNtp"; + public static final String ATTRIBUTE_TIME_ZONE = "timeZone"; + public static final String ATTRIBUTE_DST_OFFSET = "dstOffset"; + public static final String ATTRIBUTE_LOCAL_TIME = "localTime"; + public static final String ATTRIBUTE_TIME_ZONE_DATABASE = "timeZoneDatabase"; + public static final String ATTRIBUTE_NTP_SERVER_AVAILABLE = "ntpServerAvailable"; + public static final String ATTRIBUTE_TIME_ZONE_LIST_MAX_SIZE = "timeZoneListMaxSize"; + public static final String ATTRIBUTE_DST_OFFSET_LIST_MAX_SIZE = "dstOffsetListMaxSize"; + public static final String ATTRIBUTE_SUPPORTS_DNS_RESOLVE = "supportsDnsResolve"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * If the node has achieved time synchronization, this shall indicate the current time as a UTC epoch-us (Epoch Time + * in Microseconds). + * If the node has not achieved time synchronization, this shall be null. This attribute may be set when a + * SetUTCTime is received. + */ + public BigInteger utcTime; // 0 epoch-us R V + /** + * The granularity of the error that the node is willing to guarantee on the time synchronization. It is of type + * GranularityEnum. + * This value shall be set to NoTimeGranularity if UTCTime is null and shall NOT be set to NoTimeGranularity if + * UTCTime is non-null. + */ + public GranularityEnum granularity; // 1 GranularityEnum R V + /** + * The node’s time source. This attribute indicates what method the node is using to sync, whether the source uses + * NTS or not and whether the source is internal or external to the Matter network. This attribute may be used by a + * client to determine its level of trust in the UTCTime. It is of type TimeSourceEnum. + * If a node is unsure if the selected NTP server is within the Matter network, it SHOULD select one of the + * NonMatter* values. + * This value shall be set to None if UTCTime is null and shall NOT be set to None if UTCTime is non-null. + */ + public TimeSourceEnum timeSource; // 2 TimeSourceEnum R V + /** + * A Node ID, endpoint, and associated fabric index of a Node that may be used as trusted time source. See Section + * 11.17.13, “Time source prioritization”. This attribute reflects the last value set by an administrator using the + * SetTrustedTimeSource command. If the value is null, no trusted time source has yet been set. + */ + public TrustedTimeSourceStruct trustedTimeSource; // 3 TrustedTimeSourceStruct R V + /** + * The default NTP server that this Node may use if other time sources are unavailable. This attribute is settable + * by an Administrator using the SetDefaultNTP command. It SHOULD be set by the Commissioner during commissioning. + * If no default NTP server is available, the Commissioner may set this value to null. The default IANA assigned NTP + * port of 123 shall be used to access the NTP server. + * If set, the format of this attribute shall be a domain name or a static IPv6 address with no port, in text + * format, as specified in RFC 5952. The address format shall follow the recommendations in Section 4 and shall NOT + * contain a port number. + */ + public String defaultNtp; // 4 string R V + /** + * A list of time zone offsets from UTC and when they shall take effect. This attribute uses a list of time offset + * configurations to allow Nodes to handle scheduled regulatory time zone changes. This attribute shall NOT be used + * to indicate daylight savings time changes (see DSTOffset attribute for daylight savings time). + * The first entry shall have a ValidAt entry of 0. If there is a second entry, it shall have a non-zero ValidAt + * time. + * If a node supports a TimeZoneDatabase, and it has data for the given time zone Name and the given Offset matches, + * the node may update its own DSTOffset attribute to add new DST change times as required, based on the Name fields + * of the TimeZoneStruct. Administrators may add additional entries to the DSTOffset of other Nodes with the same + * time zone, if required. + * If a node does not support a TimeZoneDatabase, the Name field of the TimeZoneStruct is only applicable for + * client-side localization. In particular: + * • If the node does not support a TimeZoneDatabase, the Name field shall NOT be used to calculate the local time. + * • If the node does not support a TimeZoneDatabase, the Name field shall NOT be used to calculate DST start or end + * dates. + * When time passes, the node SHOULD remove any entries which are no longer active and change the ValidAt time for + * the currently used TimeZoneStruct list item to zero. + * This attribute shall have at least one entry. If the node does not have a default time zone and no time zone has + * been set, it may set this value to a list containing a single TimeZoneStruct with an offset of 0 (UTC) and a + * ValidAt time of 0. + */ + public List timeZone; // 5 list R V + /** + * A list of offsets to apply for daylight savings time, and their validity period. List entries shall be sorted by + * ValidStarting time. + * A list entry shall NOT have a ValidStarting time that is smaller than the ValidUntil time of the previous entry. + * There shall be at most one list entry with a null ValidUntil time and, if such an entry is present, it shall + * appear last in the list. + * Over time, the node SHOULD remove any entries which are no longer active from the list. + * Over time, if the node supports a TimeZoneDatabase and it has information available for the given time zone name, + * it may update its own list to add additional entries. + * If a time zone does not use DST, this shall be indicated by a single entry with a 0 offset and a null ValidUntil + * field. + */ + public List dstOffset; // 6 list R V + /** + * The computed current local time of the node as a epoch-us (Epoch Time in Microseconds). The value of LocalTime + * shall be the sum of the UTCTime, the offset of the currently valid TimeZoneStruct from the TimeZone attribute + * (converted to microseconds), and the offset of the currently valid DSTOffsetStruct from the DSTOffset attribute + * (converted to microseconds), if such an entry exists. + * If the node has not achieved time synchronization, this shall be null. If the node has an empty DSTOffset, this + * shall be null. + */ + public BigInteger localTime; // 7 epoch-us R V + /** + * Indicates whether the node has access to a time zone database. Nodes with a time zone database may update their + * own DSTOffset attribute to add new entries and may push DSTOffset updates to other Nodes in the same time zone as + * required. + */ + public TimeZoneDatabaseEnum timeZoneDatabase; // 8 TimeZoneDatabaseEnum R V + /** + * If the node is running an RFC 5905 NTPv4 compliant server on port 123, this value shall be True. If the node is + * not currently running an NTP server, this value shall be False. + */ + public Boolean ntpServerAvailable; // 9 bool R V + /** + * Number of supported list entries in the TimeZone attribute. This attribute may take the value of 1 or 2, where + * the optional second list entry may be used to handle scheduled regulatory time zone changes. + */ + public Integer timeZoneListMaxSize; // 10 uint8 R V + /** + * Number of supported list entries in DSTOffset attribute. This value must be at least 1. + */ + public Integer dstOffsetListMaxSize; // 11 uint8 R V + /** + * This attribute is true if the node supports resolving a domain name. DefaultNTP Address values for these nodes + * may include domain names. If this is False, the Address for a DefaultNTP shall be an IPv6 address. + */ + public Boolean supportsDnsResolve; // 12 bool R V + // Structs + + /** + * This event shall be generated when the node stops applying the current DSTOffset and there are no entries in the + * list with a larger ValidStarting time, indicating the need to possibly get new DST data. This event shall also be + * generated if the DSTOffset list is cleared either by a SetTimeZone command, or by a SetDSTOffset command with an + * empty list. + * The node shall generate this event if the node has not generated a DSTTableEmpty event in the last hour, and the + * DSTOffset list is empty when the node attempts to update its time. DSTTableEmpty events corresponding to a time + * update SHOULD NOT be generated more often than once per hour. + * There is no data for this event. + */ + public class DstTableEmpty { + public DstTableEmpty() { + } + } + + /** + * This event shall be generated when the node starts or stops applying a DST offset. + */ + public class DstStatus { + /** + * Indicates whether the current DST offset is being applied (i.e, daylight savings time is applied, as opposed + * to standard time). + */ + public Boolean dstOffsetActive; // bool + + public DstStatus(Boolean dstOffsetActive) { + this.dstOffsetActive = dstOffsetActive; + } + } + + /** + * This event shall be generated when the node changes its time zone offset or name. It shall NOT be sent for DST + * changes that are not accompanied by a time zone change. + */ + public class TimeZoneStatus { + /** + * Current time zone offset from UTC in seconds. + */ + public Integer offset; // int32 + /** + * Current time zone name. This name SHOULD use the country/city format specified by the IANA Time Zone + * Database. + */ + public String name; // string + + public TimeZoneStatus(Integer offset, String name) { + this.offset = offset; + this.name = name; + } + } + + /** + * This event shall be generated if the node has not generated a TimeFailure event in the last hour, and the node is + * unable to get a time from any source. This event SHOULD NOT be generated more often than once per hour. + */ + public class TimeFailure { + public TimeFailure() { + } + } + + /** + * This event shall be generated if the TrustedTimeSource is set to null upon fabric removal or by a + * SetTrustedTimeSource command. + * This event shall also be generated if the node has not generated a MissingTrustedTimeSource event in the last + * hour, and the node fails to update its time from the TrustedTimeSource because the TrustedTimeSource is null or + * the specified peer cannot be reached. MissingTrustedTimeSource events corresponding to a time update SHOULD NOT + * be generated more often than once per hour. + */ + public class MissingTrustedTimeSource { + public MissingTrustedTimeSource() { + } + } + + public class TrustedTimeSourceStruct { + /** + * The Fabric Index associated with the Fabric of the client which last set the value of the trusted time source + * node. + */ + public Integer fabricIndex; // fabric-idx + /** + * Node ID of the trusted time source node on the Fabric associated with the entry. + */ + public BigInteger nodeId; // node-id + /** + * Endpoint on the trusted time source node that contains the Time Synchronization cluster server. + */ + public Integer endpoint; // endpoint-no + + public TrustedTimeSourceStruct(Integer fabricIndex, BigInteger nodeId, Integer endpoint) { + this.fabricIndex = fabricIndex; + this.nodeId = nodeId; + this.endpoint = endpoint; + } + } + + public class FabricScopedTrustedTimeSourceStruct { + /** + * Node ID of the trusted time source node on the Fabric of the issuer. + */ + public BigInteger nodeId; // node-id + /** + * Endpoint on the trusted time source node that contains the Time Synchronization cluster server. This is + * provided to avoid having to do discovery of the location of that endpoint by walking over all endpoints and + * checking their Descriptor Cluster. + */ + public Integer endpoint; // endpoint-no + + public FabricScopedTrustedTimeSourceStruct(BigInteger nodeId, Integer endpoint) { + this.nodeId = nodeId; + this.endpoint = endpoint; + } + } + + public class TimeZoneStruct { + /** + * The time zone offset from UTC in seconds. + */ + public Integer offset; // int32 + /** + * The UTC time when the offset shall be applied. + */ + public BigInteger validAt; // epoch-us + /** + * The time zone name SHOULD provide a human-readable time zone name and it SHOULD use the country/city format + * specified by the IANA Time Zone Database. The Name field may be used for display. If the node supports a + * TimeZoneDatabase it may use the Name field to set its own DST offsets if it has database information for the + * supplied time zone Name and the given Offset matches. + */ + public String name; // string + + public TimeZoneStruct(Integer offset, BigInteger validAt, String name) { + this.offset = offset; + this.validAt = validAt; + this.name = name; + } + } + + public class DSTOffsetStruct { + /** + * The DST offset in seconds. Normally this is in the range of 0 to 3600 seconds (1 hour), but this field will + * accept any values in the int32 range to accommodate potential future legislation that does not fit with these + * assumptions. + */ + public Integer offset; // int32 + /** + * The UTC time when the offset shall be applied. + */ + public BigInteger validStarting; // epoch-us + /** + * The UTC time when the offset shall stop being applied. Providing a null value here indicates a permanent DST + * change. If this value is non-null the value shall be larger than the ValidStarting time. + */ + public BigInteger validUntil; // epoch-us + + public DSTOffsetStruct(Integer offset, BigInteger validStarting, BigInteger validUntil) { + this.offset = offset; + this.validStarting = validStarting; + this.validUntil = validUntil; + } + } + + // Enums + public enum GranularityEnum implements MatterEnum { + NO_TIME_GRANULARITY(0, "No Time Granularity"), + MINUTES_GRANULARITY(1, "Minutes Granularity"), + SECONDS_GRANULARITY(2, "Seconds Granularity"), + MILLISECONDS_GRANULARITY(3, "Milliseconds Granularity"), + MICROSECONDS_GRANULARITY(4, "Microseconds Granularity"); + + public final Integer value; + public final String label; + + private GranularityEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum TimeSourceEnum implements MatterEnum { + NONE(0, "None"), + UNKNOWN(1, "Unknown"), + ADMIN(2, "Admin"), + NODE_TIME_CLUSTER(3, "Node Time Cluster"), + NON_MATTER_SNTP(4, "Non Matter Sntp"), + NON_MATTER_NTP(5, "Non Matter Ntp"), + MATTER_SNTP(6, "Matter Sntp"), + MATTER_NTP(7, "Matter Ntp"), + MIXED_NTP(8, "Mixed Ntp"), + NON_MATTER_SNTPNTS(9, "Non Matter Sntpnts"), + NON_MATTER_NTPNTS(10, "Non Matter Ntpnts"), + MATTER_SNTPNTS(11, "Matter Sntpnts"), + MATTER_NTPNTS(12, "Matter Ntpnts"), + MIXED_NTPNTS(13, "Mixed Ntpnts"), + CLOUD_SOURCE(14, "Cloud Source"), + PTP(15, "Ptp"), + GNSS(16, "Gnss"); + + public final Integer value; + public final String label; + + private TimeSourceEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * It indicates what the device knows about the contents of the IANA Time Zone Database. Partial support on a device + * may be used to omit historical data, less commonly used time zones, and/or time zones not related to the region a + * product is sold in. + */ + public enum TimeZoneDatabaseEnum implements MatterEnum { + FULL(0, "Full"), + PARTIAL(1, "Partial"), + NONE(2, "None"); + + public final Integer value; + public final String label; + + private TimeZoneDatabaseEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusCodeEnum implements MatterEnum { + TIME_NOT_ACCEPTED(2, "Time Not Accepted"); + + public final Integer value; + public final String label; + + private StatusCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Allows a server to translate a UTC time to a local time using the time zone and daylight savings time (DST) + * offsets. If a server supports the TimeZone feature, it shall support the SetTimeZone and SetDSTOffset + * commands, and TimeZone and DSTOffset attributes, and shall expose the local time through the LocalTime + * attribute. + */ + public boolean timeZone; + /** + * + * Allows a node to use NTP/SNTP for time synchronization. + */ + public boolean ntpClient; + /** + * + * Allows a Node to host an NTP server for the network so that other Nodes can achieve a high accuracy time + * synchronization within the network. See Section 11.17.15, “Acting as an NTP Server”. + */ + public boolean ntpServer; + /** + * + * This node also supports a time synchronization client and can connect to and read time from other nodes. + */ + public boolean timeSyncClient; + + public FeatureMap(boolean timeZone, boolean ntpClient, boolean ntpServer, boolean timeSyncClient) { + this.timeZone = timeZone; + this.ntpClient = ntpClient; + this.ntpServer = ntpServer; + this.timeSyncClient = timeSyncClient; + } + } + + public TimeSynchronizationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 56, "TimeSynchronization"); + } + + protected TimeSynchronizationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command may be issued by Administrator to set the time. If the Commissioner does not have a valid time + * source, it may send a Granularity of NoTimeGranularity. + * Upon receipt of this command, the node may update its UTCTime attribute to match the time specified in the + * command, if the stated Granularity and TimeSource are acceptable. The node shall update its UTCTime attribute if + * its current Granularity is NoTimeGranularity. + * If the time is updated, the node shall also update its Granularity attribute based on the granularity specified + * in the command and the expected clock drift of the node. This SHOULD normally be one level lower than the stated + * command Granularity. It shall also update its TimeSource attribute to Admin. It shall also update its Last Known + * Good UTC Time as defined in Section 3.5.6.1, “Last Known Good UTC Time”. + * If the node updates its UTCTime attribute, it shall accept the command with a status code of SUCCESS. If it opts + * to not update its time, it shall fail the command with a cluster specific Status Code of TimeNotAccepted. + */ + public static ClusterCommand setUtcTime(BigInteger utcTime, GranularityEnum granularity, + TimeSourceEnum timeSource) { + Map map = new LinkedHashMap<>(); + if (utcTime != null) { + map.put("utcTime", utcTime); + } + if (granularity != null) { + map.put("granularity", granularity); + } + if (timeSource != null) { + map.put("timeSource", timeSource); + } + return new ClusterCommand("setUtcTime", map); + } + + /** + * This command shall set the TrustedTimeSource attribute. Upon receipt of this command: + * • If the TrustedTimeSource field in the command is null, the node shall set the TrustedTimeSource attribute to + * null and shall generate a MissingTrustedTimeSource event. + * • Otherwise, the node shall set the TrustedTimeSource attribute to a struct which has NodeID and Endpoint fields + * matching those in the TrustedTimeSource field and has its FabricIndex field set to the command’s accessing fabric + * index. + */ + public static ClusterCommand setTrustedTimeSource(FabricScopedTrustedTimeSourceStruct trustedTimeSource, + Integer fabricIndex) { + Map map = new LinkedHashMap<>(); + if (trustedTimeSource != null) { + map.put("trustedTimeSource", trustedTimeSource); + } + if (fabricIndex != null) { + map.put("fabricIndex", fabricIndex); + } + return new ClusterCommand("setTrustedTimeSource", map); + } + + /** + * This command is used to set the time zone of the node. + * If the given list is larger than the TimeZoneListMaxSize, the node shall respond with RESOURCE_EXHAUSTED and the + * TimeZone attribute shall NOT be updated. + * If the given list does not conform to the list requirements in TimeZone attribute the node shall respond with a + * CONSTRAINT_ERROR and the TimeZone attribute shall NOT be updated. + * If there are no errors in the list, the TimeZone field shall be copied to the TimeZone attribute. A + * TimeZoneStatus event shall be generated with the new time zone information. + * If the node supports a time zone database and it has information available for the time zone that will be + * applied, it may set its DSTOffset attribute, otherwise the DSTOffset attribute shall be set to an empty list. A + * DSTTableEmpty event shall be generated if the DSTOffset attribute is empty. A DSTStatus event shall be generated + * if the node was previously applying a DST offset. + */ + public static ClusterCommand setTimeZone(List timeZone) { + Map map = new LinkedHashMap<>(); + if (timeZone != null) { + map.put("timeZone", timeZone); + } + return new ClusterCommand("setTimeZone", map); + } + + /** + * This command is used to set the DST offsets for a node. + * • If the length of DSTOffset is larger than DSTOffsetListMaxSize, the node shall respond with RESOURCE_EXHAUSTED. + * • Else if the list entries do not conform to the list requirements for DSTOffset attribute, the node shall + * respond with CONSTRAINT_ERROR. + * If there are no errors in the list, the DSTOffset field shall be copied to the DSTOffset attribute. + * If the DSTOffset attribute change causes a corresponding change to the DST state, a DSTStatus event shall be + * generated. If the list is empty, the node shall generate a DSTTableEmpty event. + */ + public static ClusterCommand setDstOffset(List dstOffset) { + Map map = new LinkedHashMap<>(); + if (dstOffset != null) { + map.put("dstOffset", dstOffset); + } + return new ClusterCommand("setDstOffset", map); + } + + /** + * This command is used to set the DefaultNTP attribute. If the DefaultNTP Address field does not conform to the + * requirements in the DefaultNTP attribute description, the command shall fail with a status code of + * INVALID_COMMAND. If the node does not support DNS resolution (as specified in SupportsDNSResolve) and the + * provided Address is a domain name, the command shall fail with a status code of INVALID_COMMAND. Otherwise, the + * node shall set the DefaultNTP attribute to match the DefaultNTP provided in this command. + */ + public static ClusterCommand setDefaultNtp(String defaultNtp) { + Map map = new LinkedHashMap<>(); + if (defaultNtp != null) { + map.put("defaultNtp", defaultNtp); + } + return new ClusterCommand("setDefaultNtp", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "utcTime : " + utcTime + "\n"; + str += "granularity : " + granularity + "\n"; + str += "timeSource : " + timeSource + "\n"; + str += "trustedTimeSource : " + trustedTimeSource + "\n"; + str += "defaultNtp : " + defaultNtp + "\n"; + str += "timeZone : " + timeZone + "\n"; + str += "dstOffset : " + dstOffset + "\n"; + str += "localTime : " + localTime + "\n"; + str += "timeZoneDatabase : " + timeZoneDatabase + "\n"; + str += "ntpServerAvailable : " + ntpServerAvailable + "\n"; + str += "timeZoneListMaxSize : " + timeZoneListMaxSize + "\n"; + str += "dstOffsetListMaxSize : " + dstOffsetListMaxSize + "\n"; + str += "supportsDnsResolve : " + supportsDnsResolve + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TotalVolatileOrganicCompoundsConcentrationMeasurementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TotalVolatileOrganicCompoundsConcentrationMeasurementCluster.java new file mode 100644 index 00000000000..884bfcfabd7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/TotalVolatileOrganicCompoundsConcentrationMeasurementCluster.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * TotalVolatileOrganicCompoundsConcentrationMeasurement + * + * @author Dan Cunningham - Initial contribution + */ +public class TotalVolatileOrganicCompoundsConcentrationMeasurementCluster extends ConcentrationMeasurementCluster { + + public static final int CLUSTER_ID = 0x042E; + public static final String CLUSTER_NAME = "TotalVolatileOrganicCompoundsConcentrationMeasurement"; + public static final String CLUSTER_PREFIX = "totalVolatileOrganicCompoundsConcentrationMeasurement"; + + public TotalVolatileOrganicCompoundsConcentrationMeasurementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1070, "TotalVolatileOrganicCompoundsConcentrationMeasurement"); + } + + protected TotalVolatileOrganicCompoundsConcentrationMeasurementCluster(BigInteger nodeId, int endpointId, + int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/UnitLocalizationCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/UnitLocalizationCluster.java new file mode 100644 index 00000000000..ff3101c722d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/UnitLocalizationCluster.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * UnitLocalization + * + * @author Dan Cunningham - Initial contribution + */ +public class UnitLocalizationCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x002D; + public static final String CLUSTER_NAME = "UnitLocalization"; + public static final String CLUSTER_PREFIX = "unitLocalization"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_TEMPERATURE_UNIT = "temperatureUnit"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The TemperatureUnit attribute shall indicate the unit for the Node to use only when conveying temperature in + * communication to the user. If provided, this value shall take priority over any unit implied through the + * ActiveLocale Attribute. + */ + public TempUnitEnum temperatureUnit; // 0 TempUnitEnum RW VM + + // Enums + public enum TempUnitEnum implements MatterEnum { + FAHRENHEIT(0, "Fahrenheit"), + CELSIUS(1, "Celsius"), + KELVIN(2, "Kelvin"); + + public final Integer value; + public final String label; + + private TempUnitEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * The Node can be configured to use different units of temperature when conveying values to a user. + */ + public boolean temperatureUnit; + + public FeatureMap(boolean temperatureUnit) { + this.temperatureUnit = temperatureUnit; + } + } + + public UnitLocalizationCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 45, "UnitLocalization"); + } + + protected UnitLocalizationCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "temperatureUnit : " + temperatureUnit + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/UserLabelCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/UserLabelCluster.java new file mode 100644 index 00000000000..3934b2816e7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/UserLabelCluster.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * UserLabel + * + * @author Dan Cunningham - Initial contribution + */ +public class UserLabelCluster extends LabelCluster { + + public static final int CLUSTER_ID = 0x0041; + public static final String CLUSTER_NAME = "UserLabel"; + public static final String CLUSTER_PREFIX = "userLabel"; + + public UserLabelCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 65, "UserLabel"); + } + + protected UserLabelCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ValidProxiesCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ValidProxiesCluster.java new file mode 100644 index 00000000000..a6bdbf3fc97 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ValidProxiesCluster.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ValidProxies + * + * @author Dan Cunningham - Initial contribution + */ +public class ValidProxiesCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0044; + public static final String CLUSTER_NAME = "ValidProxies"; + public static final String CLUSTER_PREFIX = "validProxies"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_VALID_PROXY_LIST = "validProxyList"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * List of valid proxies that can proxy this Node. Each entry in this list is fabric-scoped. + */ + public List validProxyList; // 0 list RW + // Structs + + /** + * Encapsulates the Node ID of a Valid Proxy. + */ + public class ValidProxyStruct { + public BigInteger nodeId; // node-id + + public ValidProxyStruct(BigInteger nodeId) { + this.nodeId = nodeId; + } + } + + public ValidProxiesCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 68, "ValidProxies"); + } + + protected ValidProxiesCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used during proxy discovery, as specified in Section 9.15.7, “Proxy Discovery & Assignment + * Flow”. + */ + public static ClusterCommand getValidProxiesRequest() { + return new ClusterCommand("getValidProxiesRequest"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "validProxyList : " + validProxyList + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ValveConfigurationAndControlCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ValveConfigurationAndControlCluster.java new file mode 100644 index 00000000000..d6427965678 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/ValveConfigurationAndControlCluster.java @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * ValveConfigurationAndControl + * + * @author Dan Cunningham - Initial contribution + */ +public class ValveConfigurationAndControlCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0081; + public static final String CLUSTER_NAME = "ValveConfigurationAndControl"; + public static final String CLUSTER_PREFIX = "valveConfigurationAndControl"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_OPEN_DURATION = "openDuration"; + public static final String ATTRIBUTE_DEFAULT_OPEN_DURATION = "defaultOpenDuration"; + public static final String ATTRIBUTE_AUTO_CLOSE_TIME = "autoCloseTime"; + public static final String ATTRIBUTE_REMAINING_DURATION = "remainingDuration"; + public static final String ATTRIBUTE_CURRENT_STATE = "currentState"; + public static final String ATTRIBUTE_TARGET_STATE = "targetState"; + public static final String ATTRIBUTE_CURRENT_LEVEL = "currentLevel"; + public static final String ATTRIBUTE_TARGET_LEVEL = "targetLevel"; + public static final String ATTRIBUTE_DEFAULT_OPEN_LEVEL = "defaultOpenLevel"; + public static final String ATTRIBUTE_VALVE_FAULT = "valveFault"; + public static final String ATTRIBUTE_LEVEL_STEP = "levelStep"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the total duration, in seconds, for which the valve will remain open for this current opening. + * A value of null shall indicate the duration is not set, meaning that the valve will remain open until closed by + * the user or some other automation. + */ + public Integer openDuration; // 0 elapsed-s R V + /** + * Indicates the default duration, in seconds, for which the valve will remain open, if the OpenDuration field is + * not present in the Open command. + * A value of null shall indicate the duration is not set, meaning that the valve will remain open until closed by + * the user or some other automation. + */ + public Integer defaultOpenDuration; // 1 elapsed-s RW VO + /** + * Indicates the UTC time when the valve will close, depending on value of the OpenDuration attribute. + * Null: + * • When OpenDuration is null, or + * • When the valve does not have a synchronized UTCTime in the Time Synchronization cluster, or + * • When the valve is closed. + * When the value of this attribute is earlier or equal to the current UTC time, the valve shall automatically + * transition to its closed position. The behavior of transitioning to the closed position, shall match the behavior + * described in the Close command. + * If this attribute is not null and the Time Synchronization cluster receives a SetUTCTime command, modifying the + * current UTC time of the device, the value of this attribute shall be adjusted to match the new UTC time plus the + * value of the RemainingDuration attribute. + */ + public BigInteger autoCloseTime; // 2 epoch-us R V + /** + * Indicates the remaining duration, in seconds, until the valve closes. Null: + * • When OpenDuration is null, or + * • When the valve is closed. + * The value of this attribute shall only be reported in the following cases: + * • When it changes from null to any other value and vice versa, or + * • When it changes to 0, or + * • When it increases, or + * • When the closing time changes. + * Meaning that clients SHOULD NOT rely on the reporting of this attribute in order to keep track of the remaining + * duration, due to this attribute not being reported during regular countdown. + * When reading this attribute it shall return the remaining duration, in seconds, until the valve closes. + * When the value of this attribute counts down to 0, the valve shall automatically transition to its closed + * position. The behavior of transitioning to the closed position shall match the behavior described in the Close + * command. + */ + public Integer remainingDuration; // 3 elapsed-s R V + /** + * Indicates the current state of the valve. + * A value of null shall indicate that the current state is not known. + */ + public ValveStateEnum currentState; // 4 ValveStateEnum R V + /** + * Indicates the target state, while changing the state, of the valve. + * A value of null shall indicate that no target position is set, since the change in state is either done or + * failed. + */ + public ValveStateEnum targetState; // 5 ValveStateEnum R V + /** + * Indicates the current level of the valve as a percentage value, between fully closed and fully open. During a + * transition from one level to another level, the valve SHOULD keep this attribute updated to the best of its + * ability, in order to represent the actual level of the valve during the movement. + * A value of 100 percent shall indicate the fully open position. A value of 0 percent shall indicate the fully + * closed position. + * A value of null shall indicate that the current state is not known. + */ + public Integer currentLevel; // 6 percent R V + /** + * Indicates the target level of the valve as a percentage value, between fully closed and fully open. + * The interpretation of the percentage value is the same as for the CurrentLevel attribute. + * A value of null shall indicate that no target position is set, since the change of level is either done or + * failed. + */ + public Integer targetLevel; // 7 percent R V + /** + * Indicates the default value used for the TargetLevel attribute, when a valve transitions from the closed to the + * open state, caused by an Open command, if a TargetLevel field is not present in the Open command. + * If the LevelStep attribute is present and the value of a write interaction to this attribute field is not 100, + * the value shall be a supported value as defined by the LevelStep attribute, such that (Value received in the + * write interaction) % (Value of LevelStep attribute) equals 0. If the resulting value is not 0, the requested + * DefaultOpenLevel value is considered an unsupported value and a CONSTRAINT_ERROR status shall be returned. + */ + public Integer defaultOpenLevel; // 8 percent RW VO + /** + * Indicates any faults registered by the valve. + */ + public ValveFaultBitmap valveFault; // 9 ValveFaultBitmap R V + /** + * Indicates the step size the valve can support. + * The step size defined by this attribute is counted from 0 and the final step towards 100 may be different than + * what is defined in this attribute. For example, if the value of this attribute is 15, it results in these target + * values being supported; 0, 15, 30, 45, 60, 75, 90 and 100. + * The values of 0 and 100 shall always be supported, regardless of the value of this attribute. + */ + public Integer levelStep; // 10 uint8 R V + // Structs + + /** + * This event shall be generated when the valve state changed. For level changes, after the end of movement, for + * state changes when the new state has been reached. + */ + public class ValveStateChanged { + /** + * This field shall indicate the new state of the valve. + */ + public ValveStateEnum valveState; // ValveStateEnum + /** + * This field shall indicate the new level of the valve. + */ + public Integer valveLevel; // percent + + public ValveStateChanged(ValveStateEnum valveState, Integer valveLevel) { + this.valveState = valveState; + this.valveLevel = valveLevel; + } + } + + /** + * This event shall be generated when the valve registers or clears a fault, e.g. not being able to transition to + * the requested target level or state. + */ + public class ValveFault { + /** + * This field shall indicate the value of the ValveFault attribute, at the time this event is generated. + */ + public ValveFaultBitmap valveFault; // ValveFaultBitmap + + public ValveFault(ValveFaultBitmap valveFault) { + this.valveFault = valveFault; + } + } + + // Enums + public enum ValveStateEnum implements MatterEnum { + CLOSED(0, "Closed"), + OPEN(1, "Open"), + TRANSITIONING(2, "Transitioning"); + + public final Integer value; + public final String label; + + private ValveStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum StatusCodeEnum implements MatterEnum { + FAILURE_DUE_TO_FAULT(2, "Failure Due To Fault"); + + public final Integer value; + public final String label; + + private StatusCodeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class ValveFaultBitmap { + public boolean generalFault; + public boolean blocked; + public boolean leaking; + public boolean notConnected; + public boolean shortCircuit; + public boolean currentExceeded; + + public ValveFaultBitmap(boolean generalFault, boolean blocked, boolean leaking, boolean notConnected, + boolean shortCircuit, boolean currentExceeded) { + this.generalFault = generalFault; + this.blocked = blocked; + this.leaking = leaking; + this.notConnected = notConnected; + this.shortCircuit = shortCircuit; + this.currentExceeded = currentExceeded; + } + } + + public static class FeatureMap { + /** + * + * This feature shall indicate that the valve uses Time Synchronization and UTC time to indicate duration and + * auto close time. + * This feature shall NOT be supported unless the device supports the Time Synchronization cluster. + */ + public boolean timeSync; + /** + * + * This feature shall indicate that the valve is capable of being adjusted to a specific position, as a + * percentage, of its full range of motion. + */ + public boolean level; + + public FeatureMap(boolean timeSync, boolean level) { + this.timeSync = timeSync; + this.level = level; + } + } + + public ValveConfigurationAndControlCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 129, "ValveConfigurationAndControl"); + } + + protected ValveConfigurationAndControlCluster(BigInteger nodeId, int endpointId, int clusterId, + String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used to set the valve to its open position. + */ + public static ClusterCommand open(Integer openDuration, Integer targetLevel) { + Map map = new LinkedHashMap<>(); + if (openDuration != null) { + map.put("openDuration", openDuration); + } + if (targetLevel != null) { + map.put("targetLevel", targetLevel); + } + return new ClusterCommand("open", map); + } + + /** + * This command is used to set the valve to its closed position. + */ + public static ClusterCommand close() { + return new ClusterCommand("close"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "openDuration : " + openDuration + "\n"; + str += "defaultOpenDuration : " + defaultOpenDuration + "\n"; + str += "autoCloseTime : " + autoCloseTime + "\n"; + str += "remainingDuration : " + remainingDuration + "\n"; + str += "currentState : " + currentState + "\n"; + str += "targetState : " + targetState + "\n"; + str += "currentLevel : " + currentLevel + "\n"; + str += "targetLevel : " + targetLevel + "\n"; + str += "defaultOpenLevel : " + defaultOpenLevel + "\n"; + str += "valveFault : " + valveFault + "\n"; + str += "levelStep : " + levelStep + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WakeOnLanCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WakeOnLanCluster.java new file mode 100644 index 00000000000..0c6770cae0b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WakeOnLanCluster.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * WakeOnLan + * + * @author Dan Cunningham - Initial contribution + */ +public class WakeOnLanCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0503; + public static final String CLUSTER_NAME = "WakeOnLan"; + public static final String CLUSTER_PREFIX = "wakeOnLan"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_MAC_ADDRESS = "macAddress"; + public static final String ATTRIBUTE_LINK_LOCAL_ADDRESS = "linkLocalAddress"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the current MAC address of the device. Only 48-bit MAC Addresses shall be used for this attribute as + * required by the Wake on LAN protocol. + * Format of this attribute shall be an upper-case hex-encoded string representing the hex address, like + * 12345678ABCD. + */ + public String macAddress; // 0 string R V + /** + * Indicates the current link-local address of the device. Only 128-bit IPv6 link-local addresses shall be used for + * this attribute. + * > [!NOTE] + * > Some companies may consider MAC Address to be protected data subject to PII handling considerations and will + * therefore choose not to include it or read it. The MAC Address can often be determined using ARP in IPv4 or NDP + * in IPv6. + */ + public OctetString linkLocalAddress; // 1 ipv6adr R V + + public WakeOnLanCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1283, "WakeOnLan"); + } + + protected WakeOnLanCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "macAddress : " + macAddress + "\n"; + str += "linkLocalAddress : " + linkLocalAddress + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterHeaterManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterHeaterManagementCluster.java new file mode 100644 index 00000000000..c5a5e4b1536 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterHeaterManagementCluster.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * WaterHeaterManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class WaterHeaterManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0094; + public static final String CLUSTER_NAME = "WaterHeaterManagement"; + public static final String CLUSTER_PREFIX = "waterHeaterManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_HEATER_TYPES = "heaterTypes"; + public static final String ATTRIBUTE_HEAT_DEMAND = "heatDemand"; + public static final String ATTRIBUTE_TANK_VOLUME = "tankVolume"; + public static final String ATTRIBUTE_ESTIMATED_HEAT_REQUIRED = "estimatedHeatRequired"; + public static final String ATTRIBUTE_TANK_PERCENTAGE = "tankPercentage"; + public static final String ATTRIBUTE_BOOST_STATE = "boostState"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * Indicates the heat sources that the water heater can call on for heating. If a bit is set then the water heater + * supports the corresponding heat source. + */ + public WaterHeaterHeatSourceBitmap heaterTypes; // 0 WaterHeaterHeatSourceBitmap R V + /** + * Indicates if the water heater is heating water. If a bit is set then the corresponding heat source is active. + */ + public WaterHeaterHeatSourceBitmap heatDemand; // 1 WaterHeaterHeatSourceBitmap R V + /** + * Indicates the volume of water that the hot water tank can hold (in units of Litres). This allows an energy + * management system to estimate the required heating energy needed to reach the target temperature. + */ + public Integer tankVolume; // 2 uint16 R V + /** + * Indicates the estimated heat energy needed to raise the water temperature to the target setpoint. This can be + * computed by taking the specific heat capacity of water (4182 J/kg °C) and by knowing the current temperature of + * the water, the tank volume and target temperature. + * For example, if the target temperature was 60°C, the current temperature was 20°C and the tank volume was 100L: + * ### Mass of water = 1kg per Litre + * Total Mass = 100 x 1kg = 100kg + * Δ Temperature = (target temperature - current temperature) + * = (60°C - 20°C) = 40°C + * ### Energy required to + * heat the water to 60°C = 4182 x 40 x 100 = 16,728,000 J + * Converting Joules in to Wh of heat (divide by 3600): + * = 16,728,000 J / 3600 + * = 4647 Wh (4.65kWh) + * If the TankPercent feature is supported, then this estimate shall also take into account the percentage of the + * water in the tank which is already hot. + * > [!NOTE] + * > The electrical energy required to heat the water depends on the heating system used to heat the water. For + * example, a direct electric immersion heating element can be close to 100% efficient, so the electrical energy + * needed to heat the hot water is nearly the same as the EstimatedHeatEnergyRequired. However some forms of + * heating, such as an air-source heat pump which extracts heat from ambient air, requires much less electrical + * energy to heat hot water. Heat pumps can be produce 3kWh of heat output for 1kWh of electrical energy input. The + * conversion between heat energy and electrical energy is outside the scope of this cluster. + */ + public BigInteger estimatedHeatRequired; // 3 energy-mWh R V + /** + * Indicates an approximate level of hot water stored in the tank, which might help consumers understand the amount + * of hot water remaining in the tank. The accuracy of this attribute is manufacturer specific. + * In most hot water tanks, there is a stratification effect where the hot water layer rests above a cooler layer of + * water below. For this reason cold water is fed in at the bottom of the tank and the hot water is drawn from the + * top. + * Some water tanks might use multiple temperature probes to estimate the level of the hot water layer. A water + * heater with multiple temperature probes is likely to implement an algorithm to estimate the hot water tank + * percentage by taking into account the temperature values of each probe to determine the height of the hot water. + * However it might be possible with a single temperature probe to estimate how much hot water is left using a + * simpler algorithm: + * For example, if the target temperature was 60°C, the CurrentTemperature was 40°C from a single temperature probe + * measuring the average water temperature and the temperature of incoming cold water (COLD_WATER_TEMP) was assumed + * to be 20°C: + * TankPercentage = int(((current temperature - COLD_WATER_TEMP) / (target temperature - COLD_WATER_TEMP)) * + * 100) + * TankPercentage = min( max(TankPercentage,0), 100) + * ### TankPercentage = 50% + */ + public Integer tankPercentage; // 4 percent R V + /** + * Indicates whether the Boost, as triggered by a Boost command, is currently Active or Inactive. + * See Boost and CancelBoost commands for more details. + */ + public BoostStateEnum boostState; // 5 BoostStateEnum R V + // Structs + + /** + * This event shall be generated whenever a Boost command is accepted. + * The corresponding structure fields within the WaterHeaterBoostInfoStruct are copied from the Boost command. + */ + public class BoostStarted { + public WaterHeaterBoostInfoStruct boostInfo; // WaterHeaterBoostInfoStruct + + public BoostStarted(WaterHeaterBoostInfoStruct boostInfo) { + this.boostInfo = boostInfo; + } + } + + /** + * This event shall be generated whenever the BoostState transitions from Active to Inactive. + */ + public class BoostEnded { + public BoostEnded() { + } + } + + public class WaterHeaterBoostInfoStruct { + /** + * This field shall indicate the time period, in seconds, for which the boost state is activated. + */ + public Integer duration; // elapsed-s + /** + * This field shall indicate whether the boost state shall be automatically canceled once the hot water has + * reached either: + * • the set point temperature (from the thermostat cluster) + * • the TemporarySetpoint temperature (if specified) + * • the TargetPercentage (if specified). + */ + public Boolean oneShot; // bool + /** + * This field shall indicate that the consumer wants the water to be heated quickly. This may cause multiple + * heat sources to be activated (e.g. a heat pump and direct electric immersion heating element). + * The choice of which heat sources are activated is manufacturer specific. + */ + public Boolean emergencyBoost; // bool + /** + * This field shall indicate the target temperature to which the water will be heated. + * If included, it shall be used instead of the thermostat cluster set point temperature whilst the boost state + * is activated. + * The value of this field shall be within the constraints of the MinHeatSetpointLimit and MaxHeatSetpointLimit + * attributes (inclusive), of the thermostat cluster. + */ + public Integer temporarySetpoint; // temperature + /** + * This field shall indicate the target percentage of hot water in the tank that the TankPercentage attribute + * must reach before the heating is switched off. + */ + public Integer targetPercentage; // percent + /** + * This field shall indicate the percentage to which the hot water in the tank shall be allowed to fall before + * again beginning to reheat it. + * For example if the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% + * hot water, the tank may have hot water drawn off until only 40% hot water remains. At this point the heater + * will begin to heat back up to 80% of hot water. If this field and the OneShot field were both omitted, + * heating would begin again after any water draw which reduced the TankPercentage below 80%. + * This field shall be less than or equal to the TargetPercentage field. + */ + public Integer targetReheat; // percent + + public WaterHeaterBoostInfoStruct(Integer duration, Boolean oneShot, Boolean emergencyBoost, + Integer temporarySetpoint, Integer targetPercentage, Integer targetReheat) { + this.duration = duration; + this.oneShot = oneShot; + this.emergencyBoost = emergencyBoost; + this.temporarySetpoint = temporarySetpoint; + this.targetPercentage = targetPercentage; + this.targetReheat = targetReheat; + } + } + + // Enums + public enum BoostStateEnum implements MatterEnum { + INACTIVE(0, "Inactive"), + ACTIVE(1, "Active"); + + public final Integer value; + public final String label; + + private BoostStateEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class WaterHeaterHeatSourceBitmap { + public boolean immersionElement1; + public boolean immersionElement2; + public boolean heatPump; + public boolean boiler; + public boolean other; + + public WaterHeaterHeatSourceBitmap(boolean immersionElement1, boolean immersionElement2, boolean heatPump, + boolean boiler, boolean other) { + this.immersionElement1 = immersionElement1; + this.immersionElement2 = immersionElement2; + this.heatPump = heatPump; + this.boiler = boiler; + this.other = other; + } + } + + public static class FeatureMap { + /** + * + * Allows energy management control of the tank + */ + public boolean energyManagement; + /** + * + * Supports monitoring the percentage of hot water in the tank + */ + public boolean tankPercent; + + public FeatureMap(boolean energyManagement, boolean tankPercent) { + this.energyManagement = energyManagement; + this.tankPercent = tankPercent; + } + } + + public WaterHeaterManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 148, "WaterHeaterManagement"); + } + + protected WaterHeaterManagementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Allows a client to request that the water heater is put into a Boost state. + */ + public static ClusterCommand boost(WaterHeaterBoostInfoStruct boostInfo) { + Map map = new LinkedHashMap<>(); + if (boostInfo != null) { + map.put("boostInfo", boostInfo); + } + return new ClusterCommand("boost", map); + } + + /** + * Allows a client to cancel an ongoing Boost operation. This command has no payload. + */ + public static ClusterCommand cancelBoost() { + return new ClusterCommand("cancelBoost"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "heaterTypes : " + heaterTypes + "\n"; + str += "heatDemand : " + heatDemand + "\n"; + str += "tankVolume : " + tankVolume + "\n"; + str += "estimatedHeatRequired : " + estimatedHeatRequired + "\n"; + str += "tankPercentage : " + tankPercentage + "\n"; + str += "boostState : " + boostState + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterHeaterModeCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterHeaterModeCluster.java new file mode 100644 index 00000000000..fbb8cd5ef67 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterHeaterModeCluster.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * WaterHeaterMode + * + * @author Dan Cunningham - Initial contribution + */ +public class WaterHeaterModeCluster extends ModeBaseCluster { + + public static final int CLUSTER_ID = 0x009E; + public static final String CLUSTER_NAME = "WaterHeaterMode"; + public static final String CLUSTER_PREFIX = "waterHeaterMode"; + + // Enums + public enum ModeTag implements MatterEnum { + AUTO(0, "Auto"), + QUICK(1, "Quick"), + QUIET(2, "Quiet"), + LOW_NOISE(3, "Low Noise"), + LOW_ENERGY(4, "Low Energy"), + VACATION(5, "Vacation"), + MIN(6, "Min"), + MAX(7, "Max"), + NIGHT(8, "Night"), + DAY(9, "Day"), + OFF(16384, "Off"), + MANUAL(16385, "Manual"), + TIMED(16386, "Timed"); + + public final Integer value; + public final String label; + + private ModeTag(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public WaterHeaterModeCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 158, "WaterHeaterMode"); + } + + protected WaterHeaterModeCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterTankLevelMonitoringCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterTankLevelMonitoringCluster.java new file mode 100644 index 00000000000..5a4e60dd65c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WaterTankLevelMonitoringCluster.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; + +/** + * WaterTankLevelMonitoring + * + * @author Dan Cunningham - Initial contribution + */ +public class WaterTankLevelMonitoringCluster extends ResourceMonitoringCluster { + + public static final int CLUSTER_ID = 0x0079; + public static final String CLUSTER_NAME = "WaterTankLevelMonitoring"; + public static final String CLUSTER_PREFIX = "waterTankLevelMonitoring"; + + public WaterTankLevelMonitoringCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 121, "WaterTankLevelMonitoring"); + } + + protected WaterTankLevelMonitoringCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + @Override + public @NonNull String toString() { + String str = ""; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WiFiNetworkDiagnosticsCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WiFiNetworkDiagnosticsCluster.java new file mode 100644 index 00000000000..5bee0d345c7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WiFiNetworkDiagnosticsCluster.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * WiFiNetworkDiagnostics + * + * @author Dan Cunningham - Initial contribution + */ +public class WiFiNetworkDiagnosticsCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0036; + public static final String CLUSTER_NAME = "WiFiNetworkDiagnostics"; + public static final String CLUSTER_PREFIX = "wiFiNetworkDiagnostics"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_BSSID = "bssid"; + public static final String ATTRIBUTE_SECURITY_TYPE = "securityType"; + public static final String ATTRIBUTE_WI_FI_VERSION = "wiFiVersion"; + public static final String ATTRIBUTE_CHANNEL_NUMBER = "channelNumber"; + public static final String ATTRIBUTE_RSSI = "rssi"; + public static final String ATTRIBUTE_BEACON_LOST_COUNT = "beaconLostCount"; + public static final String ATTRIBUTE_BEACON_RX_COUNT = "beaconRxCount"; + public static final String ATTRIBUTE_PACKET_MULTICAST_RX_COUNT = "packetMulticastRxCount"; + public static final String ATTRIBUTE_PACKET_MULTICAST_TX_COUNT = "packetMulticastTxCount"; + public static final String ATTRIBUTE_PACKET_UNICAST_RX_COUNT = "packetUnicastRxCount"; + public static final String ATTRIBUTE_PACKET_UNICAST_TX_COUNT = "packetUnicastTxCount"; + public static final String ATTRIBUTE_CURRENT_MAX_RATE = "currentMaxRate"; + public static final String ATTRIBUTE_OVERRUN_COUNT = "overrunCount"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * The BSSID attribute shall indicate the BSSID for which the Wi-Fi network the Node is currently connected. + */ + public OctetString bssid; // 0 octstr R V + /** + * The SecurityType attribute shall indicate the current type of Wi-Fi security used. + */ + public SecurityTypeEnum securityType; // 1 SecurityTypeEnum R V + /** + * The WiFiVersion attribute shall indicate the current 802.11 standard version in use by the Node, per the table + * below. + */ + public WiFiVersionEnum wiFiVersion; // 2 WiFiVersionEnum R V + /** + * The ChannelNumber attribute shall indicate the channel that Wi-Fi communication is currently operating on. + */ + public Integer channelNumber; // 3 uint16 R V + /** + * The RSSI attribute shall indicate the current RSSI of the Node’s Wi-Fi radio in dBm. + */ + public Integer rssi; // 4 int8 R V + /** + * The BeaconLostCount attribute shall indicate the count of the number of missed beacons the Node has detected. If + * the Node does not have an ability to count beacons expected and not received, this value may remain set to zero. + */ + public Integer beaconLostCount; // 5 uint32 R V + /** + * The BeaconRxCount attribute shall indicate the count of the number of received beacons. The total number of + * expected beacons that could have been received during the interval since association SHOULD match the sum of + * BeaconRxCount and BeaconLostCount. If the Node does not have an ability to report count of beacons received, this + * value may remain set to zero. + */ + public Integer beaconRxCount; // 6 uint32 R V + /** + * The PacketMulticastRxCount attribute shall indicate the number of multicast packets received by the Node. + */ + public Integer packetMulticastRxCount; // 7 uint32 R V + /** + * The PacketMulticastTxCount attribute shall indicate the number of multicast packets transmitted by the Node. + */ + public Integer packetMulticastTxCount; // 8 uint32 R V + /** + * The PacketUnicastRxCount attribute shall indicate the number of unicast packets received by the Node. + */ + public Integer packetUnicastRxCount; // 9 uint32 R V + /** + * The PacketUnicastTxCount attribute shall indicate the number of unicast packets transmitted by the Node. + */ + public Integer packetUnicastTxCount; // 10 uint32 R V + /** + * The CurrentMaxRate attribute shall indicate the current maximum PHY rate of transfer of data in bits-per-second. + */ + public BigInteger currentMaxRate; // 11 uint64 R V + /** + * The OverrunCount attribute shall indicate the number of packets dropped either at ingress or egress, due to lack + * of buffer memory to retain all packets on the network interface. The OverrunCount attribute shall be reset to 0 + * upon a reboot of the Node. + */ + public BigInteger overrunCount; // 12 uint64 R V + // Structs + + /** + * The Disconnection Event shall indicate that a Node’s Wi-Fi connection has been disconnected as a result of + * de-authenticated or dis-association and indicates the reason. + */ + public class Disconnection { + /** + * This field shall contain the Reason Code field value for the Disassociation or Deauthentication event that + * caused the disconnection and the value shall align with Table 9-49 "Reason codes" of IEEE + * 802.11-2020. + */ + public Integer reasonCode; // uint16 + + public Disconnection(Integer reasonCode) { + this.reasonCode = reasonCode; + } + } + + /** + * The AssociationFailure event shall indicate that a Node has attempted to connect, or reconnect, to a Wi-Fi access + * point, but is unable to successfully associate or authenticate, after exhausting all internal retries of its + * supplicant. + */ + public class AssociationFailure { + /** + * The Status field shall be set to a value from the AssociationFailureCauseEnum. + */ + public AssociationFailureCauseEnum associationFailureCause; // AssociationFailureCauseEnum + /** + * The Status field shall be set to the Status Code value that was present in the last frame related to + * association where Status Code was not equal to zero and which caused the failure of a last trial attempt, if + * this last failure was due to one of the following Management frames: + * • Association Response (Type 0, Subtype 1) + * • Reassociation Response (Type 0, Subtype 3) + * • Authentication (Type 0, Subtype 11) + * Table 9-50 "Status codes" of IEEE 802.11-2020 contains a description of all values possible. + */ + public Integer status; // uint16 + + public AssociationFailure(AssociationFailureCauseEnum associationFailureCause, Integer status) { + this.associationFailureCause = associationFailureCause; + this.status = status; + } + } + + /** + * The ConnectionStatus Event shall indicate that a Node’s connection status to a Wi-Fi network has changed. + * Connected, in this context, shall mean that a Node acting as a Wi-Fi station is successfully associated to a + * Wi-Fi Access Point. + */ + public class ConnectionStatus { + public ConnectionStatusEnum connectionStatus; // ConnectionStatusEnum + + public ConnectionStatus(ConnectionStatusEnum connectionStatus) { + this.connectionStatus = connectionStatus; + } + } + + // Enums + public enum SecurityTypeEnum implements MatterEnum { + UNSPECIFIED(0, "Unspecified"), + NONE(1, "None"), + WEP(2, "Wep"), + WPA(3, "Wpa"), + WPA2(4, "Wpa 2"), + WPA3(5, "Wpa 3"); + + public final Integer value; + public final String label; + + private SecurityTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum WiFiVersionEnum implements MatterEnum { + A(0, "A"), + B(1, "B"), + G(2, "G"), + N(3, "N"), + AC(4, "Ac"), + AX(5, "Ax"), + AH(6, "Ah"); + + public final Integer value; + public final String label; + + private WiFiVersionEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum AssociationFailureCauseEnum implements MatterEnum { + UNKNOWN(0, "Unknown"), + ASSOCIATION_FAILED(1, "Association Failed"), + AUTHENTICATION_FAILED(2, "Authentication Failed"), + SSID_NOT_FOUND(3, "Ssid Not Found"); + + public final Integer value; + public final String label; + + private AssociationFailureCauseEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum ConnectionStatusEnum implements MatterEnum { + CONNECTED(0, "Connected"), + NOT_CONNECTED(1, "Not Connected"); + + public final Integer value; + public final String label; + + private ConnectionStatusEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class FeatureMap { + /** + * + * Node makes available the counts for the number of received and transmitted packets on the Wi-Fi interface. + */ + public boolean packetCounts; + /** + * + * Node makes available the counts for the number of errors that have occurred during the reception and + * transmission of packets on the Wi-Fi interface. + */ + public boolean errorCounts; + + public FeatureMap(boolean packetCounts, boolean errorCounts) { + this.packetCounts = packetCounts; + this.errorCounts = errorCounts; + } + } + + public WiFiNetworkDiagnosticsCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 54, "WiFiNetworkDiagnostics"); + } + + protected WiFiNetworkDiagnosticsCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Reception of this command shall reset the following attributes to 0: + * • BeaconLostCount + * • BeaconRxCount + * • PacketMulticastRxCount + * • PacketMulticastTxCount + * • PacketUnicastRxCount + * • PacketUnicastTxCount + * This command has no associated data. + */ + public static ClusterCommand resetCounts() { + return new ClusterCommand("resetCounts"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "bssid : " + bssid + "\n"; + str += "securityType : " + securityType + "\n"; + str += "wiFiVersion : " + wiFiVersion + "\n"; + str += "channelNumber : " + channelNumber + "\n"; + str += "rssi : " + rssi + "\n"; + str += "beaconLostCount : " + beaconLostCount + "\n"; + str += "beaconRxCount : " + beaconRxCount + "\n"; + str += "packetMulticastRxCount : " + packetMulticastRxCount + "\n"; + str += "packetMulticastTxCount : " + packetMulticastTxCount + "\n"; + str += "packetUnicastRxCount : " + packetUnicastRxCount + "\n"; + str += "packetUnicastTxCount : " + packetUnicastTxCount + "\n"; + str += "currentMaxRate : " + currentMaxRate + "\n"; + str += "overrunCount : " + overrunCount + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WiFiNetworkManagementCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WiFiNetworkManagementCluster.java new file mode 100644 index 00000000000..3303bc4ca85 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WiFiNetworkManagementCluster.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * WiFiNetworkManagement + * + * @author Dan Cunningham - Initial contribution + */ +public class WiFiNetworkManagementCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0451; + public static final String CLUSTER_NAME = "WiFiNetworkManagement"; + public static final String CLUSTER_PREFIX = "wiFiNetworkManagement"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_SSID = "ssid"; + public static final String ATTRIBUTE_PASSPHRASE_SURROGATE = "passphraseSurrogate"; + + public Integer clusterRevision; // 65533 ClusterRevision + /** + * Indicates the SSID of the primary Wi-Fi network provided by this device. + * A value of null shall indicate that no primary Wi-Fi network is available (e.g. because the Wi-Fi network has not + * yet been configured by the user). + * > [!NOTE] + * > The SSID in Wi-Fi is a collection of 1-32 bytes, the text encoding of which is not specified. + * Implementations must be careful to support transferring these byte strings without requiring a particular + * encoding. The most common encoding is UTF- 8, however this is just a convention. Some configurations may use + * Latin-1 or other character sets. + */ + public OctetString ssid; // 0 octstr R V + /** + * This attribute shall contain an arbitrary numeric value; this value shall increase whenever the passphrase or PSK + * associated with the primary Wi-Fi network provided by this device changes. + * A value of null shall indicate that no primary Wi-Fi network is available. + * Clients can subscribe to this attribute or compare its value to a locally cached copy to detect if a cached + * passphrase value has become stale. + * It is recommended that servers implement this attribute as either a timestamp or a counter. When implemented as a + * counter it SHOULD be initialized with a random value. + * > [!NOTE] + * > The passphrase itself is not exposed as an attribute to avoid its unintentional retrieval or caching by + * clients that use wildcard reads or otherwise routinely read all available attributes. It can be retrieved using + * the NetworkPassphraseRequest command. + */ + public BigInteger passphraseSurrogate; // 1 uint64 R M + + public WiFiNetworkManagementCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 1105, "WiFiNetworkManagement"); + } + + protected WiFiNetworkManagementCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * This command is used to request the current WPA-Personal passphrase or PSK associated with the Wi-Fi network + * provided by this device. + * If the command is not executed via a CASE session, the command shall be rejected with a status of + * UNSUPPORTED_ACCESS. + * If no primary Wi-Fi network is available (the SSID attribute is null), the command shall be rejected with a + * status of INVALID_IN_STATE. + * Otherwise a NetworkPassphraseResponse shall be generated. + */ + public static ClusterCommand networkPassphraseRequest() { + return new ClusterCommand("networkPassphraseRequest"); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "ssid : " + ssid + "\n"; + str += "passphraseSurrogate : " + passphraseSurrogate + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WindowCoveringCluster.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WindowCoveringCluster.java new file mode 100644 index 00000000000..3f518984270 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/cluster/gen/WindowCoveringCluster.java @@ -0,0 +1,635 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ + +// AUTO-GENERATED, DO NOT EDIT! + +package org.openhab.binding.matter.internal.client.dto.cluster.gen; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; + +/** + * WindowCovering + * + * @author Dan Cunningham - Initial contribution + */ +public class WindowCoveringCluster extends BaseCluster { + + public static final int CLUSTER_ID = 0x0102; + public static final String CLUSTER_NAME = "WindowCovering"; + public static final String CLUSTER_PREFIX = "windowCovering"; + public static final String ATTRIBUTE_CLUSTER_REVISION = "clusterRevision"; + public static final String ATTRIBUTE_FEATURE_MAP = "featureMap"; + public static final String ATTRIBUTE_TYPE = "type"; + public static final String ATTRIBUTE_PHYSICAL_CLOSED_LIMIT_LIFT = "physicalClosedLimitLift"; + public static final String ATTRIBUTE_PHYSICAL_CLOSED_LIMIT_TILT = "physicalClosedLimitTilt"; + public static final String ATTRIBUTE_CURRENT_POSITION_LIFT = "currentPositionLift"; + public static final String ATTRIBUTE_CURRENT_POSITION_TILT = "currentPositionTilt"; + public static final String ATTRIBUTE_NUMBER_OF_ACTUATIONS_LIFT = "numberOfActuationsLift"; + public static final String ATTRIBUTE_NUMBER_OF_ACTUATIONS_TILT = "numberOfActuationsTilt"; + public static final String ATTRIBUTE_CONFIG_STATUS = "configStatus"; + public static final String ATTRIBUTE_CURRENT_POSITION_LIFT_PERCENTAGE = "currentPositionLiftPercentage"; + public static final String ATTRIBUTE_CURRENT_POSITION_TILT_PERCENTAGE = "currentPositionTiltPercentage"; + public static final String ATTRIBUTE_OPERATIONAL_STATUS = "operationalStatus"; + public static final String ATTRIBUTE_TARGET_POSITION_LIFT_PERCENT100THS = "targetPositionLiftPercent100ths"; + public static final String ATTRIBUTE_TARGET_POSITION_TILT_PERCENT100THS = "targetPositionTiltPercent100ths"; + public static final String ATTRIBUTE_END_PRODUCT_TYPE = "endProductType"; + public static final String ATTRIBUTE_CURRENT_POSITION_LIFT_PERCENT100THS = "currentPositionLiftPercent100ths"; + public static final String ATTRIBUTE_CURRENT_POSITION_TILT_PERCENT100THS = "currentPositionTiltPercent100ths"; + public static final String ATTRIBUTE_INSTALLED_OPEN_LIMIT_LIFT = "installedOpenLimitLift"; + public static final String ATTRIBUTE_INSTALLED_CLOSED_LIMIT_LIFT = "installedClosedLimitLift"; + public static final String ATTRIBUTE_INSTALLED_OPEN_LIMIT_TILT = "installedOpenLimitTilt"; + public static final String ATTRIBUTE_INSTALLED_CLOSED_LIMIT_TILT = "installedClosedLimitTilt"; + public static final String ATTRIBUTE_MODE = "mode"; + public static final String ATTRIBUTE_SAFETY_STATUS = "safetyStatus"; + + public Integer clusterRevision; // 65533 ClusterRevision + public FeatureMap featureMap; // 65532 FeatureMap + /** + * This attribute shall identify the type of window covering. + */ + public TypeEnum type; // 0 TypeEnum R V + /** + * Indicates the maximum possible encoder position possible (Unit cm, centimeters) to position the height of the + * window covering lift. + */ + public Integer physicalClosedLimitLift; // 1 uint16 R V + /** + * Indicates the maximum possible encoder position possible (Unit 0.1°, tenths of a degree) to position the angle of + * the window covering tilt. + */ + public Integer physicalClosedLimitTilt; // 2 uint16 R V + /** + * Indicates the actual lift position (Unit cm, centimeters) of the window covering from the fully-open position. + */ + public Integer currentPositionLift; // 3 uint16 R V + /** + * Indicates the actual tilt position (Unit 0.1°, tenths of a degree) of the window covering from the fully-open + * position. + */ + public Integer currentPositionTilt; // 4 uint16 R V + /** + * Indicates the total number of lift/slide actuations applied to the window covering since the device was + * installed. + */ + public Integer numberOfActuationsLift; // 5 uint16 R V + /** + * Indicates the total number of tilt actuations applied to the window covering since the device was installed. + */ + public Integer numberOfActuationsTilt; // 6 uint16 R V + /** + * This attribute specifies the configuration and status information of the window covering. + * To change settings, devices shall write to the Mode attribute. The behavior causing the setting or clearing of + * each bit is vendor specific. + */ + public ConfigStatusBitmap configStatus; // 7 ConfigStatusBitmap R V + /** + * Indicates the actual position as a percentage from 0% to 100% with 1% default step. This attribute is equal to + * CurrentPositionLiftPercent100ths attribute divided by 100. + */ + public Integer currentPositionLiftPercentage; // 8 percent R V + /** + * Indicates the actual position as a percentage from 0% to 100% with 1% default step. This attribute is equal to + * CurrentPositionTiltPercent100ths attribute divided by 100. + */ + public Integer currentPositionTiltPercentage; // 9 percent R V + /** + * Indicates the currently ongoing operations and applies to all type of devices. + */ + public OperationalStatusBitmap operationalStatus; // 10 OperationalStatusBitmap R V + /** + * Indicates the position where the window covering lift will go or is moving to as a percentage (Unit 0.01%). + */ + public Integer targetPositionLiftPercent100ths; // 11 percent100ths R V + /** + * Indicates the position where the window covering tilt will go or is moving to as a percentage (Unit 0.01%). + */ + public Integer targetPositionTiltPercent100ths; // 12 percent100ths R V + /** + * This attribute SHOULD provide more detail about the product type than can be determined from the main category + * indicated by the Type attribute. + * The table below helps to match the EndProductType attribute with the Type attribute. + */ + public EndProductTypeEnum endProductType; // 13 EndProductTypeEnum R V + /** + * Indicates the actual position as a percentage with a minimal step of 0.01%. E.g Max 10000 equals 100.00%. + */ + public Integer currentPositionLiftPercent100ths; // 14 percent100ths R V + /** + * Indicates the actual position as a percentage with a minimal step of 0.01%. E.g Max 10000 equals 100.00%. + */ + public Integer currentPositionTiltPercent100ths; // 15 percent100ths R V + /** + * Indicates the open limit for lifting the window covering whether position (in centimeters) is encoded or timed. + */ + public Integer installedOpenLimitLift; // 16 uint16 R V + /** + * Indicates the closed limit for lifting the window covering whether position (in centimeters) is encoded or timed. + */ + public Integer installedClosedLimitLift; // 17 uint16 R V + /** + * Indicates the open limit for tilting the window covering whether position (in tenth of a degree) is encoded or + * timed. + */ + public Integer installedOpenLimitTilt; // 18 uint16 R V + /** + * Indicates the closed limit for tilting the window covering whether position (in tenth of a degree) is encoded or + * timed. + */ + public Integer installedClosedLimitTilt; // 19 uint16 R V + /** + * The Mode attribute allows configuration of the window covering, such as: reversing the motor direction, placing + * the window covering into calibration mode, placing the motor into maintenance mode, disabling the network, and + * disabling status LEDs. + * In the case a device does not support or implement a specific mode, e.g. the device has a specific installation + * method and reversal is not relevant or the device does not include a maintenance mode, any write interaction to + * the Mode attribute, with an unsupported mode bit or any out of bounds bits set, must be ignored and a response + * containing the status of CONSTRAINT_ERROR will be returned. + */ + public ModeBitmap mode; // 23 ModeBitmap RW VM + /** + * The SafetyStatus attribute reflects the state of the safety sensors and the common issues preventing movements. + * By default for nominal operation all flags are cleared (0). A device might support none, one or several bit flags + * from this attribute (all optional). + */ + public SafetyStatusBitmap safetyStatus; // 26 SafetyStatusBitmap R V + + // Enums + public enum TypeEnum implements MatterEnum { + ROLLERSHADE(0, "Rollershade"), + ROLLERSHADE2MOTOR(1, "Rollershade 2 Motor"), + ROLLERSHADE_EXTERIOR(2, "Rollershade Exterior"), + ROLLERSHADE_EXTERIOR2MOTOR(3, "Rollershade Exterior 2 Motor"), + DRAPERY(4, "Drapery"), + AWNING(5, "Awning"), + SHUTTER(6, "Shutter"), + TILT_BLIND_TILT_ONLY(7, "Tilt Blind Tilt Only"), + TILT_BLIND_LIFT(8, "Tilt Blind Lift"), + PROJECTOR_SCREEN(9, "Projector Screen"), + UNKNOWN(255, "Unknown"); + + public final Integer value; + public final String label; + + private TypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + public enum EndProductTypeEnum implements MatterEnum { + ROLLER_SHADE(0, "Roller Shade"), + ROMAN_SHADE(1, "Roman Shade"), + BALLOON_SHADE(2, "Balloon Shade"), + WOVEN_WOOD(3, "Woven Wood"), + PLEATED_SHADE(4, "Pleated Shade"), + CELLULAR_SHADE(5, "Cellular Shade"), + LAYERED_SHADE(6, "Layered Shade"), + LAYERED_SHADE2D(7, "Layered Shade 2 D"), + SHEER_SHADE(8, "Sheer Shade"), + TILT_ONLY_INTERIOR_BLIND(9, "Tilt Only Interior Blind"), + INTERIOR_BLIND(10, "Interior Blind"), + VERTICAL_BLIND_STRIP_CURTAIN(11, "Vertical Blind Strip Curtain"), + INTERIOR_VENETIAN_BLIND(12, "Interior Venetian Blind"), + EXTERIOR_VENETIAN_BLIND(13, "Exterior Venetian Blind"), + LATERAL_LEFT_CURTAIN(14, "Lateral Left Curtain"), + LATERAL_RIGHT_CURTAIN(15, "Lateral Right Curtain"), + CENTRAL_CURTAIN(16, "Central Curtain"), + ROLLER_SHUTTER(17, "Roller Shutter"), + EXTERIOR_VERTICAL_SCREEN(18, "Exterior Vertical Screen"), + AWNING_TERRACE_PATIO(19, "Awning Terrace Patio"), + AWNING_VERTICAL_SCREEN(20, "Awning Vertical Screen"), + TILT_ONLY_PERGOLA(21, "Tilt Only Pergola"), + SWINGING_SHUTTER(22, "Swinging Shutter"), + SLIDING_SHUTTER(23, "Sliding Shutter"), + UNKNOWN(255, "Unknown"); + + public final Integer value; + public final String label; + + private EndProductTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + /** + * Values for OperationalStatus attribute fields. + */ + public enum MovementStatus implements MatterEnum { + STOPPED(0, "Stopped"), + OPENING(1, "Opening"), + CLOSING(2, "Closing"); + + public final Integer value; + public final String label; + + private MovementStatus(Integer value, String label) { + this.value = value; + this.label = label; + } + + @Override + public Integer getValue() { + return value; + } + + @Override + public String getLabel() { + return label; + } + } + + // Bitmaps + public static class ConfigStatusBitmap { + /** + * Device is operational. + * This bit shall indicate whether the window covering is operational for regular use: + * • 0 = Not Operational + * • 1 = Operational + */ + public boolean operational; + public boolean onlineReserved; + /** + * The lift movement is reversed. + * This bit shall indicate whether the lift movement is reversed: + * • 0 = Lift movement is normal + * • 1 = Lift movement is reversed + */ + public boolean liftMovementReversed; + /** + * Supports the PositionAwareLift feature (PA_LF). + * This bit shall indicate whether the window covering supports the PositionAwareLift feature: + * • 0 = Lift control is not position aware + * • 1 = Lift control is position aware (PA_LF) + */ + public boolean liftPositionAware; + /** + * Supports the PositionAwareTilt feature (PA_TL). + * This bit shall indicate whether the window covering supports the PositionAwareTilt feature: + * • 0 = Tilt control is not position aware + * • 1 = Tilt control is position aware (PA_TL) + */ + public boolean tiltPositionAware; + /** + * Uses an encoder for lift. + * This bit shall indicate whether a position aware controlled window covering is employing an encoder for + * positioning the height of the window covering: + * • 0 = Timer Controlled + * • 1 = Encoder Controlled + */ + public boolean liftEncoderControlled; + /** + * Uses an encoder for tilt. + * This bit shall indicate whether a position aware controlled window covering is employing an encoder for + * tilting the window covering: + * • 0 = Timer Controlled + * • 1 = Encoder Controlled + */ + public boolean tiltEncoderControlled; + + public ConfigStatusBitmap(boolean operational, boolean onlineReserved, boolean liftMovementReversed, + boolean liftPositionAware, boolean tiltPositionAware, boolean liftEncoderControlled, + boolean tiltEncoderControlled) { + this.operational = operational; + this.onlineReserved = onlineReserved; + this.liftMovementReversed = liftMovementReversed; + this.liftPositionAware = liftPositionAware; + this.tiltPositionAware = tiltPositionAware; + this.liftEncoderControlled = liftEncoderControlled; + this.tiltEncoderControlled = tiltEncoderControlled; + } + } + + public static class ModeBitmap { + /** + * Reverse the lift direction. + * This bit shall control the motor direction: + * • 0 = Lift movement is normal + * • 1 = Lift movement is reversed + */ + public boolean motorDirectionReversed; + /** + * Perform a calibration. + * This bit shall set the window covering into calibration mode: + * • 0 = Normal mode + * • 1 = Calibration mode + */ + public boolean calibrationMode; + /** + * Freeze all motions for maintenance. + * This bit shall set the window covering into maintenance mode: + * • 0 = Normal mode + * • 1 = Maintenance mode + */ + public boolean maintenanceMode; + /** + * Control the LEDs feedback. + * This bit shall control feedback LEDs: + * • 0 = LEDs are off + * • 1 = LEDs will display feedback + */ + public boolean ledFeedback; + + public ModeBitmap(boolean motorDirectionReversed, boolean calibrationMode, boolean maintenanceMode, + boolean ledFeedback) { + this.motorDirectionReversed = motorDirectionReversed; + this.calibrationMode = calibrationMode; + this.maintenanceMode = maintenanceMode; + this.ledFeedback = ledFeedback; + } + } + + /** + * The OperationalStatusBitmap is using several internal operational state fields (composed of 2 bits) following + * this definition: + * • 00b = Currently not moving + * • 01b = Currently opening (e.g. moving from closed to open). + * • 10b = Currently closing (e.g. moving from open to closed). + * • 11b = Reserved + */ + public static class OperationalStatusBitmap { + /** + * Global operational state. + * These bits shall indicate in which direction the covering is currently moving or if it has stopped. Global + * operational state shall always reflect the overall motion of the device. + */ + public short global; + /** + * Lift operational state. + * These bits shall indicate in which direction the covering’s lift is currently moving or if it has stopped. + */ + public short lift; + /** + * Tilt operational state. + * These bits shall indicate in which direction the covering’s tilt is currently moving or if it has stopped. + */ + public short tilt; + + public OperationalStatusBitmap(short global, short lift, short tilt) { + this.global = global; + this.lift = lift; + this.tilt = tilt; + } + } + + public static class SafetyStatusBitmap { + public boolean remoteLockout; + public boolean tamperDetection; + public boolean failedCommunication; + public boolean positionFailure; + public boolean thermalProtection; + public boolean obstacleDetected; + public boolean power; + public boolean stopInput; + public boolean motorJammed; + public boolean hardwareFailure; + public boolean manualOperation; + public boolean protection; + + public SafetyStatusBitmap(boolean remoteLockout, boolean tamperDetection, boolean failedCommunication, + boolean positionFailure, boolean thermalProtection, boolean obstacleDetected, boolean power, + boolean stopInput, boolean motorJammed, boolean hardwareFailure, boolean manualOperation, + boolean protection) { + this.remoteLockout = remoteLockout; + this.tamperDetection = tamperDetection; + this.failedCommunication = failedCommunication; + this.positionFailure = positionFailure; + this.thermalProtection = thermalProtection; + this.obstacleDetected = obstacleDetected; + this.power = power; + this.stopInput = stopInput; + this.motorJammed = motorJammed; + this.hardwareFailure = hardwareFailure; + this.manualOperation = manualOperation; + this.protection = protection; + } + } + + public static class FeatureMap { + /** + * + * The Lift feature applies to window coverings that lift up and down (e.g. for a roller shade, Up and Down is + * lift Open and Close) or slide left to right (e.g. for a sliding curtain, Left and Right is lift Open and + * Close). + */ + public boolean lift; + /** + * + * The Tilt feature applies to window coverings with vertical or horizontal strips. + */ + public boolean tilt; + /** + * + * Position aware lift control is supported. + */ + public boolean positionAwareLift; + /** + * + * The percentage attributes shall indicate the position as a percentage between the InstalledOpenLimits and + * InstalledClosedLimits attributes of the window covering starting at the open (0.00%). + * As a general rule, absolute positioning (in centimeters or tenth of a degrees) SHOULD NOT be supported for + * new implementations. + */ + public boolean absolutePosition; + /** + * + * Position aware tilt control is supported. + */ + public boolean positionAwareTilt; + + public FeatureMap(boolean lift, boolean tilt, boolean positionAwareLift, boolean absolutePosition, + boolean positionAwareTilt) { + this.lift = lift; + this.tilt = tilt; + this.positionAwareLift = positionAwareLift; + this.absolutePosition = absolutePosition; + this.positionAwareTilt = positionAwareTilt; + } + } + + public WindowCoveringCluster(BigInteger nodeId, int endpointId) { + super(nodeId, endpointId, 258, "WindowCovering"); + } + + protected WindowCoveringCluster(BigInteger nodeId, int endpointId, int clusterId, String clusterName) { + super(nodeId, endpointId, clusterId, clusterName); + } + + // commands + /** + * Upon receipt of this command, the window covering will adjust its position so the physical lift/slide and tilt is + * at the maximum open/up position. This will happen as fast as possible. The server attributes shall be updated as + * follows: + * if the PositionAware feature is supported: + * • TargetPositionLiftPercent100ths attribute shall be set to 0.00%. + * • TargetPositionTiltPercent100ths attribute shall be set to 0.00%. + * The server positioning attributes will follow the movements, once the movement has successfully finished, the + * server attributes shall be updated as follows: + * if the PositionAware feature is supported: + * • CurrentPositionLiftPercent100ths attribute shall be 0.00%. + * • CurrentPositionLiftPercentage attribute shall be 0%. + * • CurrentPositionTiltPercent100ths attribute shall be 0.00%. + * • CurrentPositionTiltPercentage attribute shall be 0%. + * if the AbsolutePosition feature is supported: + * • CurrentPositionLift attribute shall be equal to the InstalledOpenLimitLift attribute. + * • CurrentPositionTilt attribute shall be equal to the InstalledOpenLimitTilt attribute. + */ + public static ClusterCommand upOrOpen() { + return new ClusterCommand("upOrOpen"); + } + + /** + * Upon receipt of this command, the window covering will adjust its position so the physical lift/slide and tilt is + * at the maximum closed/down position. This will happen as fast as possible. The server attributes supported shall + * be updated as follows: + * if the PositionAware feature is supported: + * • TargetPositionLiftPercent100ths attribute shall be set to 100.00%. + * • TargetPositionTiltPercent100ths attribute shall be set to 100.00%. + * The server positioning attributes will follow the movements, once the movement has successfully finished, the + * server attributes shall be updated as follows: + * if the PositionAware feature is supported: + * • CurrentPositionLiftPercent100ths attribute shall be 100.00%. + * • CurrentPositionLiftPercentage attribute shall be 100%. + * • CurrentPositionTiltPercent100ths attribute shall be 100.00%. + * • CurrentPositionTiltPercentage attribute shall be 100%. + * if the AbsolutePosition feature is supported: + * • CurrentPositionLift attribute shall be equal to the InstalledClosedLimitLift attribute. + * • CurrentPositionTilt attribute shall be equal to the InstalledClosedLimitTilt attribute. + */ + public static ClusterCommand downOrClose() { + return new ClusterCommand("downOrClose"); + } + + /** + * Upon receipt of this command, the window covering will stop any adjusting to the physical tilt and lift/slide + * that is currently occurring. The server attributes supported shall be updated as follows: + * • TargetPositionLiftPercent100ths attribute will be set to CurrentPositionLiftPercent100ths attribute value. + * • TargetPositionTiltPercent100ths attribute will be set to CurrentPositionTiltPercent100ths attribute value. + */ + public static ClusterCommand stopMotion() { + return new ClusterCommand("stopMotion"); + } + + public static ClusterCommand goToLiftValue(Integer liftValue) { + Map map = new LinkedHashMap<>(); + if (liftValue != null) { + map.put("liftValue", liftValue); + } + return new ClusterCommand("goToLiftValue", map); + } + + /** + * Upon receipt of this command, the server will adjust the window covering to the lift/slide percentage specified + * in the payload of this command. + * If the command includes LiftPercent100thsValue, then TargetPositionLiftPercent100ths attribute shall be set to + * LiftPercent100thsValue. Otherwise the TargetPositionLiftPercent100ths attribute shall be set to + * LiftPercentageValue * 100. + * If a client includes LiftPercent100thsValue in the command, the LiftPercentageValue shall be set to + * LiftPercent100thsValue / 100, so a legacy server which only supports LiftPercentageValue (not + * LiftPercent100thsValue) has a value to set the target position. + * If the server does not support the PositionAware feature, then a zero percentage shall be treated as a UpOrOpen + * command and a non-zero percentage shall be treated as an DownOrClose command. If the device is only a tilt + * control device, then the command SHOULD be ignored and a UNSUPPORTED_COMMAND status SHOULD be returned. + */ + public static ClusterCommand goToLiftPercentage(Integer liftPercent100thsValue) { + Map map = new LinkedHashMap<>(); + if (liftPercent100thsValue != null) { + map.put("liftPercent100thsValue", liftPercent100thsValue); + } + return new ClusterCommand("goToLiftPercentage", map); + } + + public static ClusterCommand goToTiltValue(Integer tiltValue) { + Map map = new LinkedHashMap<>(); + if (tiltValue != null) { + map.put("tiltValue", tiltValue); + } + return new ClusterCommand("goToTiltValue", map); + } + + /** + * Upon receipt of this command, the server will adjust the window covering to the tilt percentage specified in the + * payload of this command. + * If the command includes TiltPercent100thsValue, then TargetPositionTiltPercent100ths attribute shall be set to + * TiltPercent100thsValue. Otherwise the TargetPositionTiltPercent100ths attribute shall be set to + * TiltPercentageValue * 100. + * If a client includes TiltPercent100thsValue in the command, the TiltPercentageValue shall be set to + * TiltPercent100thsValue / 100, so a legacy server which only supports TiltPercentageValue (not + * TiltPercent100thsValue) has a value to set the target position. + * If the server does not support the PositionAware feature, then a zero percentage shall be treated as a UpOrOpen + * command and a non-zero percentage shall be treated as an DownOrClose command. If the device is only a tilt + * control device, then the command SHOULD be ignored and a UNSUPPORTED_COMMAND status SHOULD be returned. + */ + public static ClusterCommand goToTiltPercentage(Integer tiltPercent100thsValue) { + Map map = new LinkedHashMap<>(); + if (tiltPercent100thsValue != null) { + map.put("tiltPercent100thsValue", tiltPercent100thsValue); + } + return new ClusterCommand("goToTiltPercentage", map); + } + + @Override + public @NonNull String toString() { + String str = ""; + str += "clusterRevision : " + clusterRevision + "\n"; + str += "featureMap : " + featureMap + "\n"; + str += "type : " + type + "\n"; + str += "physicalClosedLimitLift : " + physicalClosedLimitLift + "\n"; + str += "physicalClosedLimitTilt : " + physicalClosedLimitTilt + "\n"; + str += "currentPositionLift : " + currentPositionLift + "\n"; + str += "currentPositionTilt : " + currentPositionTilt + "\n"; + str += "numberOfActuationsLift : " + numberOfActuationsLift + "\n"; + str += "numberOfActuationsTilt : " + numberOfActuationsTilt + "\n"; + str += "configStatus : " + configStatus + "\n"; + str += "currentPositionLiftPercentage : " + currentPositionLiftPercentage + "\n"; + str += "currentPositionTiltPercentage : " + currentPositionTiltPercentage + "\n"; + str += "operationalStatus : " + operationalStatus + "\n"; + str += "targetPositionLiftPercent100ths : " + targetPositionLiftPercent100ths + "\n"; + str += "targetPositionTiltPercent100ths : " + targetPositionTiltPercent100ths + "\n"; + str += "endProductType : " + endProductType + "\n"; + str += "currentPositionLiftPercent100ths : " + currentPositionLiftPercent100ths + "\n"; + str += "currentPositionTiltPercent100ths : " + currentPositionTiltPercent100ths + "\n"; + str += "installedOpenLimitLift : " + installedOpenLimitLift + "\n"; + str += "installedClosedLimitLift : " + installedClosedLimitLift + "\n"; + str += "installedOpenLimitTilt : " + installedOpenLimitTilt + "\n"; + str += "installedClosedLimitTilt : " + installedClosedLimitTilt + "\n"; + str += "mode : " + mode + "\n"; + str += "safetyStatus : " + safetyStatus + "\n"; + return str; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/ActiveSessionInformation.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/ActiveSessionInformation.java new file mode 100644 index 00000000000..2b5eee39569 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/ActiveSessionInformation.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import java.math.BigInteger; + +/** + * ActiveSessionInformation describes an active session with a peer. + * + * @author Dan Cunningham - Initial contribution + */ +public class ActiveSessionInformation { + public String name; + public BigInteger nodeId; + public String peerNodeId; + public ExposedFabricInformation fabric; + public boolean isPeerActive; + public boolean secure; + public Long lastInteractionTimestamp; // Use Long for undefined (null) values + public Long lastActiveTimestamp; // Use Long for undefined (null) values + public int numberOfActiveSubscriptions; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/AttributeChangedMessage.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/AttributeChangedMessage.java new file mode 100644 index 00000000000..2dbee385a35 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/AttributeChangedMessage.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +/** + * AttributeChangedMessage + * + * @author Dan Cunningham - Initial contribution + */ +public class AttributeChangedMessage { + public Path path; + public Long version; + public Object value; + + public AttributeChangedMessage() { + } + + /** + * @param path + * @param version + * @param value + */ + public AttributeChangedMessage(Path path, Long version, Object value) { + this.path = path; + this.version = version; + this.value = value; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeCommissionState.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeCommissionState.java new file mode 100644 index 00000000000..6f6a23ec699 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeCommissionState.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import org.openhab.binding.matter.internal.client.dto.PairingCodes; + +/** + * BridgeCommissionState + * + * @author Dan Cunningham - Initial contribution + */ +public class BridgeCommissionState { + public PairingCodes pairingCodes; + public Boolean commissioningWindowOpen; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventAttributeChanged.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventAttributeChanged.java new file mode 100644 index 00000000000..2f3053bc02f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventAttributeChanged.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +/** + * BridgeEventAttributeChanged + * + * @author Dan Cunningham - Initial contribution + */ +public class BridgeEventAttributeChanged extends BridgeEventMessage { + public AttributeChangedData data; + + public class AttributeChangedData { + public String endpointId; + public String clusterName; + public String attributeName; + public Object data; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventMessage.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventMessage.java new file mode 100644 index 00000000000..aebd8f37ee5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventMessage.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +/** + * BridgeEventMessage + * + * @author Dan Cunningham - Initial contribution + */ +public class BridgeEventMessage { + public String type; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventTriggered.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventTriggered.java new file mode 100644 index 00000000000..a271f04e3bf --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/BridgeEventTriggered.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import java.util.Map; + +/** + * BridgeEventTriggered + * + * @author Dan Cunningham - Initial contribution + */ +public class BridgeEventTriggered extends BridgeEventMessage { + + public TriggerEvent data; + + public class TriggerEvent { + public String eventName; + public Map data; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Event.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Event.java new file mode 100644 index 00000000000..1ec6a9f469b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Event.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import com.google.gson.JsonElement; + +/** + * Event + * + * @author Dan Cunningham - Initial contribution + */ +public class Event { + public String type; + public JsonElement data; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/EventTriggeredMessage.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/EventTriggeredMessage.java new file mode 100644 index 00000000000..4533976d577 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/EventTriggeredMessage.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +/** + * EventTriggeredMessage is a message that is sent when an matter event is triggered. + * + * @author Dan Cunningham - Initial contribution + */ +public class EventTriggeredMessage { + public Path path; + public TriggerEvent[] events; + + public EventTriggeredMessage() { + } + + public EventTriggeredMessage(Path path, TriggerEvent[] events) { + this.path = path; + this.events = events; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/ExposedFabricInformation.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/ExposedFabricInformation.java new file mode 100644 index 00000000000..4a6509b75ef --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/ExposedFabricInformation.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import java.math.BigInteger; + +/** + * ExposedFabricInformation represents Matter Fabric information from a device. + * + * @author Dan Cunningham - Initial contribution + */ +public class ExposedFabricInformation { + public Integer fabricIndex; + public String fabricId; + public BigInteger nodeId; + public String rootNodeId; + public byte[] operationalId; + public byte[] rootPublicKey; + public Integer rootVendorId; + public byte[] rootCert; + public byte[] identityProtectionKey; + public byte[] operationalIdentityProtectionKey; + public byte[] intermediateCACert; + public byte[] operationalCert; + public String label; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Message.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Message.java new file mode 100644 index 00000000000..8ae864f0e04 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Message.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import com.google.gson.JsonObject; + +/** + * Message + * + * @author Dan Cunningham - Initial contribution + */ +public class Message { + public String type; + public JsonObject message; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeDataMessage.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeDataMessage.java new file mode 100644 index 00000000000..740befbf46c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeDataMessage.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import org.openhab.binding.matter.internal.client.dto.Node; + +/** + * NodeDataMessage + * + * @author Dan Cunningham - Initial contribution + */ +public class NodeDataMessage { + + public Node node; + + public NodeDataMessage(Node node) { + this.node = node; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeInitializedMessage.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeInitializedMessage.java new file mode 100644 index 00000000000..489d90a9821 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeInitializedMessage.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import org.openhab.binding.matter.internal.client.dto.Node; + +/** + * NodeInitializedMessage + * + * @author Dan Cunningham - Initial contribution + */ +public class NodeInitializedMessage { + + public Node node; + + public NodeInitializedMessage(Node node) { + this.node = node; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeState.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeState.java new file mode 100644 index 00000000000..442facfb6ec --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeState.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * NodeState + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public enum NodeState { + /** Node is connected, but may not be fully initialized / subscribed. */ + CONNECTED("Connected"), + /** + * Node is disconnected. Data are stale and interactions will most likely return an error. If controller + * instance + * is still active then the device will be reconnected once it is available again. + */ + DISCONNECTED("Disconnected"), + + /** Node is reconnecting. Data are stale. It is yet unknown if the reconnection is successful. */ + RECONNECTING("Reconnecting"), + + /** + * The node could not be connected and the controller is now waiting for a MDNS announcement and tries every 10 + * minutes to reconnect. + */ + WAITINGFORDEVICEDISCOVERY("WaitingForDeviceDiscovery"), + + /** + * Node structure has changed (Endpoints got added or also removed). Data are up-to-date. + * This State information will only be fired when the subscribeAllAttributesAndEvents option is set to true. + */ + STRUCTURECHANGED("StructureChanged"), + + /** + * The node was just Decommissioned. + */ + DECOMMISSIONED("Decommissioned"); + + private String state = ""; + + NodeState(String state) { + this.state = state; + } + + @Override + public String toString() { + return state; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeStateMessage.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeStateMessage.java new file mode 100644 index 00000000000..b593223fe8f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/NodeStateMessage.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import java.math.BigInteger; + +/** + * NodeStateMessage + * + * @author Dan Cunningham - Initial contribution + */ +public class NodeStateMessage { + public BigInteger nodeId; + public NodeState state; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Path.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Path.java new file mode 100644 index 00000000000..ed8fad49f3f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Path.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import java.math.BigInteger; + +/** + * Path + * + * @author Dan Cunningham - Initial contribution + */ +public class Path { + public BigInteger nodeId; + public Integer endpointId; + public Integer clusterId; + public String clusterName; + public Integer attributeId; + public String attributeName; + public Integer eventId; + public String eventName; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Request.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Request.java new file mode 100644 index 00000000000..d032651e797 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Request.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +/** + * Request + * + * @author Dan Cunningham - Initial contribution + */ +public class Request { + + public String id; + public String namespace; + public String function; + public Object args[]; + + public Request(String requestId, String namespace, String function, Object args[]) { + this.id = requestId; + this.namespace = namespace; + this.function = function; + this.args = args; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Response.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Response.java new file mode 100644 index 00000000000..64321f70bce --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/Response.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +import com.google.gson.JsonElement; + +/** + * Response + * + * @author Dan Cunningham - Initial contribution + */ +public class Response { + public String type; + public String id; + public JsonElement result; + public String error; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/TriggerEvent.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/TriggerEvent.java new file mode 100644 index 00000000000..43097a81eba --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/client/dto/ws/TriggerEvent.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client.dto.ws; + +/** + * TriggerEvent + * + * + * @author Dan Cunningham - Initial contribution + */ +public class TriggerEvent { + public String eventNumber; + public int priority; + public String epochTimestamp; + public Object data; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/ControllerConfiguration.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/ControllerConfiguration.java new file mode 100644 index 00000000000..08b2dc9eeb4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/ControllerConfiguration.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link ControllerConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ControllerConfiguration { + + public String nodeId = "0"; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/EndpointConfiguration.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/EndpointConfiguration.java new file mode 100644 index 00000000000..77d9b4a4ed7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/EndpointConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link EndpointConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class EndpointConfiguration { + + public int endpointId = 0; + public int pollInterval = 1800; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/NodeConfiguration.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/NodeConfiguration.java new file mode 100644 index 00000000000..a49e0157655 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/config/NodeConfiguration.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.config; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * The {@link NodeConfiguration} class contains fields mapping thing configuration parameters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class NodeConfiguration { + + public String nodeId = "0"; + public int pollInterval = 1800; +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/MatterControllerClient.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/MatterControllerClient.java new file mode 100644 index 00000000000..1a3a2064981 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/MatterControllerClient.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller; + +import java.lang.reflect.Type; +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.MatterWebsocketClient; +import org.openhab.binding.matter.internal.client.MatterWebsocketService; +import org.openhab.binding.matter.internal.client.dto.PairingCodes; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OperationalCredentialsCluster; +import org.openhab.binding.matter.internal.client.dto.ws.ActiveSessionInformation; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +/** + * The {@link MatterControllerClient} is a client for the Matter Controller. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterControllerClient extends MatterWebsocketClient { + + public static final int PAIRING_TIMEOUT_SECONDS = 60 * 3; + + public void connect(MatterWebsocketService wss, BigInteger nodeId, String controllerName, String storagePath) { + Map params = Map.of("nodeId", nodeId.toString(), "controllerName", controllerName, + "storagePath", storagePath); + connectWhenReady(wss, params); + } + + /** + * Get all nodes that are commissioned / paired to this controller + * + * @return a future that returns a list of node IDs + */ + public CompletableFuture> getCommissionedNodeIds() { + CompletableFuture future = sendMessage("nodes", "listNodes", new Object[0]); + return future.thenApply(obj -> { + List nodes = gson.fromJson(obj, new TypeToken>() { + }.getType()); + return nodes != null ? nodes : Collections.emptyList(); + }); + } + + /** + * Initialize a commissioned node, wait for connectionTimeoutMilliseconds for the node to connect before returning + * + * @param nodeId the node ID to initialize + * @param connectionTimeoutMilliseconds the timeout in milliseconds to wait for the node to connect + * @return a future that completes when the node is initialized + */ + public CompletableFuture initializeNode(BigInteger nodeId, Integer connectionTimeoutMilliseconds) { + // add 1 second delay to the message timeout to allow the function to complete + CompletableFuture future = sendMessage("nodes", "initializeNode", + new Object[] { nodeId, connectionTimeoutMilliseconds }, connectionTimeoutMilliseconds / 1000 + 1); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Request all cluster attribute data for the node from the controller, the actual data will be sent via a + * NodeDataListener event + * + * @param nodeId the node ID to request data for + * @return a future that completes when the data is requested + */ + public CompletableFuture requestAllNodeData(BigInteger nodeId) { + CompletableFuture future = sendMessage("nodes", "requestAllData", new Object[] { nodeId }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Request all cluster attribute data for a single endpoint and its children + * + * @param nodeId the node ID to request data for + * @param endpointId the endpoint ID to request data for + * @return a future that completes when the data is requested + */ + public CompletableFuture requestEndpointData(BigInteger nodeId, Integer endpointId) { + CompletableFuture future = sendMessage("nodes", "requestEndpointData", + new Object[] { nodeId, endpointId }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Pair a node using a pairing code, either a manual pairing code or a matter QR code (starts with MT:) + * + * @param code the pairing code to pair with + * @return a future that completes when the node is paired (or fails) + */ + public CompletableFuture pairNode(String code) { + String[] parts = code.trim().split(" "); + CompletableFuture future = null; + if (parts.length == 2) { + future = sendMessage("nodes", "pairNode", new Object[] { "", parts[0], parts[1] }, PAIRING_TIMEOUT_SECONDS); + } else { + // MT is a matter QR code, other wise remove any dashes in a manual pairing code + String pairCode = parts[0].toUpperCase().indexOf("MT:") == 0 ? parts[0] : parts[0].replaceAll("-", ""); + future = sendMessage("nodes", "pairNode", new Object[] { pairCode }, PAIRING_TIMEOUT_SECONDS); + } + return future.thenApply(obj -> { + return new BigInteger(obj.getAsString()); + }); + } + + /** + * Remove a node from the controller + * + * @param nodeId the node ID to remove + * @return a future that completes when the node is removed + */ + public CompletableFuture removeNode(BigInteger nodeId) { + CompletableFuture future = sendMessage("nodes", "removeNode", new Object[] { nodeId }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Reconnect a node to the controller + * + * @param nodeId the node ID to reconnect + * @return a future that completes when the node is reconnected + */ + public CompletableFuture reconnectNode(BigInteger nodeId) { + CompletableFuture future = sendMessage("nodes", "reconnectNode", new Object[] { nodeId }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Get the pairing codes for a node + * + * @param nodeId the node ID to get the pairing codes for + * @return a future that completes when the pairing codes are retrieved + * @throws JsonParseException when completing the future if the pairing codes cannot be deserialized + */ + public CompletableFuture enhancedCommissioningWindow(BigInteger nodeId) { + CompletableFuture future = sendMessage("nodes", "enhancedCommissioningWindow", + new Object[] { nodeId }); + return future.thenApply(obj -> { + PairingCodes codes = gson.fromJson(obj, PairingCodes.class); + if (codes == null) { + throw new JsonParseException("Could not deserialize pairing codes"); + } + return codes; + }); + } + + /** + * Disconnect a node from the controller + * + * @param nodeId the node ID to disconnect + * @return a future that completes when the node is disconnected + */ + public CompletableFuture disconnectNode(BigInteger nodeId) { + CompletableFuture future = sendMessage("nodes", "disconnectNode", new Object[] { nodeId }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Get the fabrics for a node, fabrics are the list of matter networks the node is joined to + * + * @param nodeId the node ID to get the fabrics for + * @return a future that completes when the fabrics are retrieved or an exception is thrown + * @throws JsonParseException when completing the future if the fabrics cannot be deserialized + */ + public CompletableFuture> getFabrics(BigInteger nodeId) { + Object[] clusterArgs = { String.valueOf(nodeId) }; + CompletableFuture future = sendMessage("nodes", "getFabrics", clusterArgs); + return future.thenApply(obj -> { + Type listType = new TypeToken>() { + }.getType(); + List list = gson.fromJson(obj, listType); + if (list == null) { + throw new JsonParseException("Could not deserialize fabrics"); + } + return list; + }); + } + + /** + * Remove a fabric from a node, fabrics are identified by an index in the matter specification + * + * @param nodeId the node ID to remove the fabric from + * @param index the index of the fabric to remove + * @return a future that completes when the fabric is removed + */ + public CompletableFuture removeFabric(BigInteger nodeId, Integer index) { + CompletableFuture future = sendMessage("nodes", "removeFabric", new Object[] { nodeId, index }); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Send a command to a cluster + * + * @param nodeId the node ID to send the command to + * @param endpointId the endpoint ID to send the command to + * @param clusterName the cluster name to send the command to + * @param command the command to send + * @return a future that completes when the command is sent + */ + public CompletableFuture clusterCommand(BigInteger nodeId, Integer endpointId, String clusterName, + ClusterCommand command) { + Object[] clusterArgs = { String.valueOf(nodeId), endpointId, clusterName, command.commandName, command.args }; + CompletableFuture future = sendMessage("clusters", "command", clusterArgs); + return future; + } + + /** + * Write an attribute to a cluster + * + * @param nodeId the node ID to write the attribute to + * @param endpointId the endpoint ID to write the attribute to + * @param clusterName the cluster name to write the attribute to + * @param attributeName the attribute name to write + * @param value the value to write + * @return a future that completes when the attribute is written + */ + public CompletableFuture clusterWriteAttribute(BigInteger nodeId, Integer endpointId, String clusterName, + String attributeName, String value) { + Object[] clusterArgs = { String.valueOf(nodeId), endpointId, clusterName, attributeName, value }; + CompletableFuture future = sendMessage("clusters", "writeAttribute", clusterArgs); + return future.thenAccept(obj -> { + // Do nothing, just to complete the future + }); + } + + /** + * Read all attributes from a cluster + * + * @param type the class type to deserialize the cluster to + * @param nodeId the node ID to read the cluster from + * @param endpointId the endpoint ID to read the cluster from + * @param clusterId the cluster ID to read the cluster from + * @return a future that completes when the cluster is read + * @throws JsonParseException when completing the future if the cluster cannot be deserialized + */ + public CompletableFuture readCluster(Class type, BigInteger nodeId, + Integer endpointId, Integer clusterId) { + Object[] clusterArgs = { String.valueOf(nodeId), endpointId, clusterId }; + CompletableFuture future = sendMessage("clusters", "readCluster", clusterArgs); + return future.thenApply(obj -> { + @Nullable + T result = gson.fromJson(obj, type); + if (result == null) { + throw new JsonParseException("Could not deserialize cluster data"); + } + return result; + }); + } + + /** + * Read an attribute from a cluster + * + * @param nodeId the node ID to read the attribute from + * @param endpointId the endpoint ID to read the attribute from + * @param clusterName the cluster name to read the attribute from + * @param attributeName the attribute name to read + * @return a future that completes when the attribute is read + */ + public CompletableFuture clusterReadAttribute(BigInteger nodeId, Integer endpointId, String clusterName, + String attributeName) { + Object[] clusterArgs = { String.valueOf(nodeId), endpointId, clusterName, attributeName }; + CompletableFuture future = sendMessage("clusters", "readAttribute", clusterArgs); + return future.thenApply(obj -> { + return obj.getAsString(); + }); + } + + /** + * Get the session information for the controller + * + * @return a future that completes when the session information is retrieved + * @throws JsonParseException when completing the future if the session information cannot be deserialized + */ + public CompletableFuture getSessionInformation() { + CompletableFuture future = sendMessage("nodes", "sessionInformation", new Object[0]); + return future.thenApply(obj -> { + ActiveSessionInformation[] sessions = gson.fromJson(obj, ActiveSessionInformation[].class); + return sessions == null ? new ActiveSessionInformation[0] : sessions; + }); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/BooleanStateConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/BooleanStateConverter.java new file mode 100644 index 00000000000..32f839fa6de --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/BooleanStateConverter.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_BOOLEANSTATE_STATEVALUE; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_BOOLEANSTATE_STATEVALUE; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link BooleanStateCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class BooleanStateConverter extends GenericConverter { + + public BooleanStateConverter(BooleanStateCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_BOOLEANSTATE_STATEVALUE), CoreItemFactory.SWITCH) + .withType(CHANNEL_BOOLEANSTATE_STATEVALUE).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case BooleanStateCluster.ATTRIBUTE_STATE_VALUE: + if (message.value instanceof Boolean booleanValue) { + updateState(CHANNEL_ID_BOOLEANSTATE_STATEVALUE, OnOffType.from(booleanValue)); + + } + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_BOOLEANSTATE_STATEVALUE, OnOffType.from(initializingCluster.stateValue)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ColorControlConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ColorControlConverter.java new file mode 100644 index 00000000000..5a4233d0083 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ColorControlConverter.java @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster.MatterEnum; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster.EnhancedColorModeEnum; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.UnDefType; +import org.openhab.core.util.ColorUtil; + +/** + * A converter for translating {@link ColorControlCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + * @author Chris Jackson - Original Zigbee binding color logic functions borrowed here + * + */ +@NonNullByDefault +public class ColorControlConverter extends GenericConverter { + + // We will wait up for this time to receive multiple color updates before we update the color values + private static final int UPDATE_DELAY = 500; + // These are the default values, as well as the min and max limits for the color temperature mireds defined in the + // Matter spec + private static final int MAX_MIREDS = 65279; + private static final int MIN_MIREDS = 1; // this was 0 until matter 1.4 + // These are sane defaults that should be used if the device does not provide a valid range (aka if using default + // MIN / MAX) + private static final int MAX_DEFAULT_MIREDS = 667; // 1500K + private static final int MIN_DEFAULT_MIREDS = 154; + protected boolean supportsHue = false; + protected boolean supportsColorTemperature = false; + private LevelControlCluster.OptionsBitmap levelControlOptionsBitmap = new LevelControlCluster.OptionsBitmap(true, + true); + private boolean lastOnOff = true; + private HSBType lastHSB = new HSBType("0,0,0"); + private @Nullable ScheduledFuture colorUpdateTimer = null; + private ScheduledExecutorService colorUpdateScheduler = Executors.newSingleThreadScheduledExecutor(); + // Color attributes from the device + private int colorTempPhysicalMinMireds = 0; + private int colorTempPhysicalMaxMireds = 0; + private EnhancedColorModeEnum lastColorMode = EnhancedColorModeEnum.CURRENT_X_AND_CURRENT_Y; + private int lastHue = 0; + private int lastSaturation = 0; + private int lastX = 0; + private int lastY = 0; + private int lastColorTemperatureMireds; + // These states are used to track the state of the color updates, once a state is ready we will set the color. + private ColorUpdateState hueSaturationState = ColorUpdateState.READY; + private ColorUpdateState xyState = ColorUpdateState.READY; + private ColorUpdateState colorTemperatureState = ColorUpdateState.READY; + + public ColorControlConverter(ColorControlCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + supportsHue = initializingCluster.featureMap.hueSaturation; + supportsColorTemperature = initializingCluster.featureMap.colorTemperature; + // The inovelli device for example sends a max mireds of 65279 (matter spec max value), which means they did not + // actually set the max mireds, but this should never really exceed 667 (1500K) + Integer maxMireds = initializingCluster.colorTempPhysicalMaxMireds; + colorTempPhysicalMaxMireds = (maxMireds == null || maxMireds >= MAX_MIREDS) ? MAX_DEFAULT_MIREDS + : Math.min(maxMireds, MAX_DEFAULT_MIREDS); + // the inovelli device for example sends a min mireds of 0 (matter 1.3 default) which means they did not + // actually set the min mireds, but this should never really be less than 154 (6500K)in real life + Integer minMireds = initializingCluster.colorTempPhysicalMinMireds; + colorTempPhysicalMinMireds = (minMireds == null || minMireds <= MIN_MIREDS) ? MIN_DEFAULT_MIREDS + : Math.max(minMireds, MIN_DEFAULT_MIREDS); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Map map = new HashMap<>(); + + map.put(ChannelBuilder.create(new ChannelUID(channelGroupUID, CHANNEL_ID_COLOR_COLOR), CoreItemFactory.COLOR) + .withType(CHANNEL_COLOR_COLOR).build(), null); + + // see Matter spec 3.2.6.1 For more information on color temperature + if (initializingCluster.featureMap.colorTemperature) { + map.put(ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_COLOR_TEMPERATURE), CoreItemFactory.DIMMER) + .withType(CHANNEL_COLOR_TEMPERATURE).build(), null); + StateDescription stateDescription = null; + if (colorTempPhysicalMinMireds < colorTempPhysicalMaxMireds) { + stateDescription = StateDescriptionFragmentBuilder.create().withPattern("%.0f mirek") + .withMinimum(BigDecimal.valueOf(colorTempPhysicalMinMireds)) + .withMaximum(BigDecimal.valueOf(colorTempPhysicalMaxMireds)).build().toStateDescription(); + } + map.put(ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_COLOR_TEMPERATURE_ABS), "Number:Temperature") + .withType(CHANNEL_COLOR_TEMPERATURE_ABS).build(), stateDescription); + } + return map; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof HSBType color) { + PercentType brightness = color.getBrightness(); + if (brightness.equals(PercentType.ZERO)) { + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, OnOffCluster.off()); + } else { + ClusterCommand levelCommand = LevelControlCluster.moveToLevelWithOnOff( + ValueUtils.percentToLevel(brightness), 0, levelControlOptionsBitmap, levelControlOptionsBitmap); + handler.sendClusterCommand(endpointNumber, LevelControlCluster.CLUSTER_NAME, levelCommand); + if (supportsHue) { + changeColorHueSaturation(color); + } else { + changeColorXY(color); + } + } + } else if (command instanceof OnOffType onOffType) { + ClusterCommand onOffCommand = onOffType == OnOffType.ON ? OnOffCluster.on() : OnOffCluster.off(); + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, onOffCommand); + } else if (command instanceof PercentType percentType) { + if (channelUID.getIdWithoutGroup().equals(CHANNEL_ID_COLOR_TEMPERATURE)) { + ClusterCommand tempCommand = ColorControlCluster.moveToColorTemperature( + percentTypeToMireds(percentType), 0, initializingCluster.options, initializingCluster.options); + handler.sendClusterCommand(endpointNumber, ColorControlCluster.CLUSTER_NAME, tempCommand); + if (!lastOnOff) { + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, OnOffCluster.on()); + } + } else { + if (percentType.equals(PercentType.ZERO)) { + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, OnOffCluster.off()); + } else { + ClusterCommand levelCommand = LevelControlCluster.moveToLevelWithOnOff( + ValueUtils.percentToLevel(percentType), 0, levelControlOptionsBitmap, + levelControlOptionsBitmap); + handler.sendClusterCommand(endpointNumber, LevelControlCluster.CLUSTER_NAME, levelCommand); + } + } + } else if (channelUID.getIdWithoutGroup().equals(CHANNEL_ID_COLOR_TEMPERATURE_ABS) + && command instanceof DecimalType decimal) { + ClusterCommand tempCommand = ColorControlCluster.moveToColorTemperature(decimal.intValue(), 0, + initializingCluster.options, initializingCluster.options); + handler.sendClusterCommand(endpointNumber, ColorControlCluster.CLUSTER_NAME, tempCommand); + } else if (channelUID.getIdWithoutGroup().equals(CHANNEL_ID_COLOR_TEMPERATURE_ABS) + && command instanceof QuantityType quantity) { + quantity = quantity.toInvertibleUnit(Units.MIRED); + if (quantity != null) { + ClusterCommand tempCommand = ColorControlCluster.moveToColorTemperature(quantity.intValue(), 0, + initializingCluster.options, initializingCluster.options); + handler.sendClusterCommand(endpointNumber, ColorControlCluster.CLUSTER_NAME, tempCommand); + } + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + logger.debug("OnEvent: {} with value {}", message.path.attributeName, message.value); + Integer numberValue = message.value instanceof Number number ? number.intValue() : 0; + boolean colorChanged = false; + switch (message.path.attributeName) { + case ColorControlCluster.ATTRIBUTE_CURRENT_X: + lastX = numberValue; + xyState = xyState.next(); + colorChanged = true; + break; + case ColorControlCluster.ATTRIBUTE_CURRENT_Y: + lastY = numberValue; + xyState = xyState.next(); + colorChanged = true; + break; + case ColorControlCluster.ATTRIBUTE_CURRENT_HUE: + lastHue = numberValue; + hueSaturationState = hueSaturationState.next(); + colorChanged = true; + break; + case ColorControlCluster.ATTRIBUTE_CURRENT_SATURATION: + lastSaturation = numberValue; + hueSaturationState = hueSaturationState.next(); + colorChanged = true; + break; + case ColorControlCluster.ATTRIBUTE_COLOR_TEMPERATURE_MIREDS: + if (numberValue >= colorTempPhysicalMinMireds && numberValue <= colorTempPhysicalMaxMireds) { + lastColorTemperatureMireds = numberValue; + colorTemperatureState = ColorUpdateState.READY; + colorChanged = true; + } else { + throw new IllegalArgumentException("Invalid color temperature mireds: " + numberValue + + " must be between " + colorTempPhysicalMinMireds + " and " + colorTempPhysicalMaxMireds); + } + break; + case ColorControlCluster.ATTRIBUTE_COLOR_MODE: + case ColorControlCluster.ATTRIBUTE_ENHANCED_COLOR_MODE: + EnhancedColorModeEnum newColorMode = lastColorMode; + if (message.value instanceof ColorControlCluster.ColorModeEnum colorMode) { + try { + newColorMode = MatterEnum.fromValue(EnhancedColorModeEnum.class, colorMode.value); + } catch (IllegalArgumentException e) { + logger.debug("Unknown color mode: {}", numberValue); + } + } + if (message.value instanceof ColorControlCluster.EnhancedColorModeEnum enhancedColorMode) { + newColorMode = enhancedColorMode; + } + if (newColorMode != lastColorMode) { + lastColorMode = newColorMode; + switch (lastColorMode) { + case CURRENT_HUE_AND_CURRENT_SATURATION: + hueSaturationState = ColorUpdateState.WAITING1; + break; + case CURRENT_X_AND_CURRENT_Y: + xyState = ColorUpdateState.WAITING1; + break; + case COLOR_TEMPERATURE_MIREDS: + colorTemperatureState = ColorUpdateState.WAITING1; + break; + default: + break; + } + colorChanged = true; + } + break; + case ColorControlCluster.ATTRIBUTE_ENHANCED_CURRENT_HUE: + logger.debug("enhancedCurrentHue not supported yet"); + break; + case LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL: + updateBrightness(ValueUtils.levelToPercent(numberValue)); + break; + case OnOffCluster.ATTRIBUTE_ON_OFF: + updateOnOff((Boolean) message.value); + break; + default: + logger.debug("Unknown attribute {}", message.path.attributeName); + } + if (colorChanged) { + updateColor(); + } + super.onEvent(message); + } + + @Override + public void initState() { + initState(true, null); + } + + public void initState(boolean onOff, @Nullable LevelControlCluster levelControlCluster) { + Integer brightness = 100; + if (levelControlCluster != null) { + if (levelControlCluster.currentLevel != null) { + brightness = levelControlCluster.currentLevel; + } + if (levelControlCluster.options != null) { + levelControlOptionsBitmap = levelControlCluster.options; + } + } + lastHSB = new HSBType(lastHSB.getHue(), lastHSB.getSaturation(), ValueUtils.levelToPercent(brightness)); + lastColorMode = Optional.ofNullable(initializingCluster.enhancedColorMode).orElseGet( + () -> MatterEnum.fromValue(EnhancedColorModeEnum.class, initializingCluster.colorMode.value)); + lastOnOff = onOff; + lastX = initializingCluster.currentX != null ? initializingCluster.currentX : 0; + lastY = initializingCluster.currentY != null ? initializingCluster.currentY : 0; + lastHue = initializingCluster.currentHue != null ? initializingCluster.currentHue : 0; + lastSaturation = initializingCluster.currentSaturation != null ? initializingCluster.currentSaturation : 0; + lastColorTemperatureMireds = initializingCluster.colorTemperatureMireds != null + ? initializingCluster.colorTemperatureMireds + : 154; + + updateColor(); + } + + /** + * A color device will send a colorMode attribute when changing from temperature mode or to color mode and vice + * versa. We will update the color values after we wait for the device to send multiple update within the timer + * duration or if we get all states for a required color mode + * + * Calling this will cancel any existing timer and start a new one to wait for the next update + */ + private void updateColor() { + logger.debug("updateColor: lastColorMode {}", lastColorMode); + cancelUpdateTimer(); + switch (lastColorMode) { + case CURRENT_HUE_AND_CURRENT_SATURATION: + if (hueSaturationState.isReady()) { + updateColorHSB(); + } else { + startUpdateTimer(this::updateColorHSB); + } + temperatureModeToColor(); + break; + case CURRENT_X_AND_CURRENT_Y: + if (xyState.isReady()) { + updateColorXY(); + } else { + startUpdateTimer(this::updateColorXY); + } + temperatureModeToColor(); + break; + case COLOR_TEMPERATURE_MIREDS: + if (colorTemperatureState.isReady()) { + updateColorTemperature(); + } else { + startUpdateTimer(this::updateColorTemperature); + } + break; + default: + logger.debug("Unknown color mode: {}", lastColorMode); + } + } + + private synchronized void startUpdateTimer(Runnable updateFunction) { + cancelUpdateTimer(); + logger.debug("starting color timer"); + colorUpdateTimer = colorUpdateScheduler.schedule(updateFunction, UPDATE_DELAY, TimeUnit.MILLISECONDS); + } + + private void cancelUpdateTimer() { + @Nullable + ScheduledFuture colorUpdateTimer = this.colorUpdateTimer; + if (colorUpdateTimer != null) { + colorUpdateTimer.cancel(true); + } + } + + private void updateOnOff(boolean onOff) { + lastOnOff = onOff; + HSBType hsb = new HSBType(lastHSB.getHue(), lastHSB.getSaturation(), + lastOnOff ? lastHSB.getBrightness() : new PercentType(0)); + updateState(CHANNEL_ID_COLOR_COLOR, hsb); + } + + private void updateBrightness(PercentType brightness) { + // Extra temp variable to avoid thread sync concurrency issues on lastHSB + HSBType oldHSB = lastHSB; + HSBType newHSB = new HSBType(oldHSB.getHue(), oldHSB.getSaturation(), brightness); + lastHSB = newHSB; + if (!lastOnOff) { + updateState(CHANNEL_ID_COLOR_COLOR, + new HSBType(newHSB.getHue(), newHSB.getSaturation(), new PercentType(0))); + } else { + updateState(CHANNEL_ID_COLOR_COLOR, newHSB); + } + } + + // These color functions are borrowed from the Zigbee openHAB binding and modified for Matter + + private void updateColorHSB() { + float hueValue = lastHue * 360.0f / 254.0f; + float saturationValue = lastSaturation * 100.0f / 254.0f; + DecimalType hue = new DecimalType(Float.valueOf(hueValue).toString()); + PercentType saturation = new PercentType(Float.valueOf(saturationValue).toString()); + updateColorHSB(hue, saturation); + hueSaturationState = ColorUpdateState.READY; + } + + private void updateColorHSB(DecimalType hue, PercentType saturation) { + // Extra temp variable to avoid thread sync concurrency issues on lastHSB + HSBType oldHSB = lastHSB; + HSBType newHSB = new HSBType(hue, saturation, oldHSB.getBrightness()); + lastHSB = newHSB; + if (!lastOnOff) { + updateState(CHANNEL_ID_COLOR_COLOR, + new HSBType(newHSB.getHue(), newHSB.getSaturation(), new PercentType(0))); + } else { + updateState(CHANNEL_ID_COLOR_COLOR, newHSB); + } + } + + private void updateColorXY() { + float xValue = lastX / 65536.0f; + float yValue = lastY / 65536.0f; + PercentType x = new PercentType(Float.valueOf(xValue * 100.0f).toString()); + PercentType y = new PercentType(Float.valueOf(yValue * 100.0f).toString()); + updateColorXY(x, y); + xyState = ColorUpdateState.READY; + } + + private void updateColorXY(PercentType x, PercentType y) { + try { + HSBType color = ColorUtil.xyToHsb(new double[] { x.floatValue() / 100.0f, y.floatValue() / 100.0f }); + updateColorHSB(color.getHue(), color.getSaturation()); + } catch (IllegalArgumentException e) { + updateState(CHANNEL_ID_COLOR_COLOR, UnDefType.UNDEF); + } + } + + private void updateColorTemperature() { + if (lastOnOff) + updateState(CHANNEL_ID_COLOR_TEMPERATURE, miredsToPercentType(lastColorTemperatureMireds)); + updateState(CHANNEL_ID_COLOR_TEMPERATURE_ABS, + QuantityType.valueOf(Double.valueOf(lastColorTemperatureMireds), Units.MIRED)); + colorTemperatureState = ColorUpdateState.READY; + colorModeToTemperature(); + } + + private void changeColorHueSaturation(HSBType color) { + int hue = (int) (color.getHue().floatValue() * 254.0f / 360.0f + 0.5f); + int saturation = ValueUtils.percentToLevel(color.getSaturation()); + handler.sendClusterCommand(endpointNumber, ColorControlCluster.CLUSTER_NAME, ColorControlCluster + .moveToHueAndSaturation(hue, saturation, 0, initializingCluster.options, initializingCluster.options)); + } + + private void changeColorXY(HSBType color) { + PercentType xy[] = color.toXY(); + int x = (int) (xy[0].floatValue() / 100.0f * 65536.0f + 0.5f); // up to 65279 + int y = (int) (xy[1].floatValue() / 100.0f * 65536.0f + 0.5f); // up to 65279 + handler.sendClusterCommand(endpointNumber, ColorControlCluster.CLUSTER_NAME, + ColorControlCluster.moveToColor(x, y, 0, initializingCluster.options, initializingCluster.options)); + } + + private PercentType miredsToPercentType(Integer mireds) { + int mired = Math.max(colorTempPhysicalMinMireds, Math.min(colorTempPhysicalMaxMireds, mireds)); + return new PercentType((int) (((double) (mired - colorTempPhysicalMinMireds) + / (colorTempPhysicalMaxMireds - colorTempPhysicalMinMireds) * 100))); + } + + private Integer percentTypeToMireds(PercentType percent) { + return (int) ((percent.doubleValue() / 100) * (colorTempPhysicalMaxMireds - colorTempPhysicalMinMireds)) + + colorTempPhysicalMinMireds; + } + + private void colorModeToTemperature() { + try { + HSBType color = ColorUtil.xyToHsb(ColorUtil.kelvinToXY(1000000.0 / lastColorTemperatureMireds)); + updateColorHSB(color.getHue(), color.getSaturation()); + } catch (IllegalArgumentException | IndexOutOfBoundsException e) { + updateState(CHANNEL_ID_COLOR_COLOR, UnDefType.UNDEF); + } + } + + private void temperatureModeToColor() { + updateState(CHANNEL_ID_COLOR_TEMPERATURE, UnDefType.UNDEF); + updateState(CHANNEL_ID_COLOR_TEMPERATURE_ABS, UnDefType.UNDEF); + } + + /** + * Tracks the state of the color updates, once a state is ready we will set the color. + * X/Y and Hue/Saturation potentially waits for 2 updates before setting the color + */ + enum ColorUpdateState { + READY, + WAITING1, + WAITING2; + + // move to the next waiting state + public ColorUpdateState next() { + return switch (this) { + case READY -> WAITING2; + case WAITING1 -> WAITING2; + case WAITING2 -> READY; + }; + } + + public boolean isReady() { + return this == READY; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ConverterRegistry.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ConverterRegistry.java new file mode 100644 index 00000000000..efdd2ccf0ff --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ConverterRegistry.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DoorLockCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ElectricalEnergyMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ElectricalPowerMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.FanControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.IlluminanceMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ModeSelectCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OccupancySensingCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.PowerSourceCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.RelativeHumidityMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.SwitchCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.TemperatureMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThermostatCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThreadBorderRouterManagementCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThreadNetworkDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WiFiNetworkDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WindowCoveringCluster; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; + +/** + * A registry of converters for translating Matter clusters to openHAB channels. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ConverterRegistry { + private static final Map>> CONVERTERS = new HashMap<>(); + + static { + ConverterRegistry.registerConverter(ColorControlCluster.CLUSTER_ID, ColorControlConverter.class); + ConverterRegistry.registerConverter(LevelControlCluster.CLUSTER_ID, LevelControlConverter.class); + ConverterRegistry.registerConverter(ModeSelectCluster.CLUSTER_ID, ModeSelectConverter.class); + ConverterRegistry.registerConverter(OnOffCluster.CLUSTER_ID, OnOffConverter.class); + ConverterRegistry.registerConverter(SwitchCluster.CLUSTER_ID, SwitchConverter.class); + ConverterRegistry.registerConverter(ThermostatCluster.CLUSTER_ID, ThermostatConverter.class); + ConverterRegistry.registerConverter(WindowCoveringCluster.CLUSTER_ID, WindowCoveringConverter.class); + ConverterRegistry.registerConverter(PowerSourceCluster.CLUSTER_ID, PowerSourceConverter.class); + ConverterRegistry.registerConverter(FanControlCluster.CLUSTER_ID, FanControlConverter.class); + ConverterRegistry.registerConverter(RelativeHumidityMeasurementCluster.CLUSTER_ID, + RelativeHumidityMeasurementConverter.class); + ConverterRegistry.registerConverter(TemperatureMeasurementCluster.CLUSTER_ID, + TemperatureMeasurementConverter.class); + ConverterRegistry.registerConverter(OccupancySensingCluster.CLUSTER_ID, OccupancySensingConverter.class); + ConverterRegistry.registerConverter(IlluminanceMeasurementCluster.CLUSTER_ID, + IlluminanceMeasurementConverter.class); + ConverterRegistry.registerConverter(BooleanStateCluster.CLUSTER_ID, BooleanStateConverter.class); + ConverterRegistry.registerConverter(WiFiNetworkDiagnosticsCluster.CLUSTER_ID, + WiFiNetworkDiagnosticsConverter.class); + ConverterRegistry.registerConverter(DoorLockCluster.CLUSTER_ID, DoorLockConverter.class); + ConverterRegistry.registerConverter(ElectricalPowerMeasurementCluster.CLUSTER_ID, + ElectricalPowerMeasurementConverter.class); + ConverterRegistry.registerConverter(ElectricalEnergyMeasurementCluster.CLUSTER_ID, + ElectricalEnergyMeasurementConverter.class); + ConverterRegistry.registerConverter(ThreadNetworkDiagnosticsCluster.CLUSTER_ID, + ThreadNetworkDiagnosticsConverter.class); + ConverterRegistry.registerConverter(ThreadBorderRouterManagementCluster.CLUSTER_ID, + ThreadBorderRouterManagementConverter.class); + } + + public static void registerConverter(Integer clusterId, + Class> converter) { + CONVERTERS.put(clusterId, converter); + } + + public static GenericConverter createConverter(BaseCluster cluster, + MatterBaseThingHandler handler, int endpointNumber, String labelPrefix) + throws ConverterCreationException, NoConverterFoundException { + Class> clazz = CONVERTERS.get(cluster.id); + if (clazz == null) { + throw new NoConverterFoundException("No converter found for cluster " + cluster.id); + } + + Class[] constructorParameterTypes = new Class[] { cluster.getClass(), MatterBaseThingHandler.class, + int.class, String.class }; + Constructor> constructor; + try { + constructor = clazz.getConstructor(constructorParameterTypes); + return constructor.newInstance(cluster, handler, endpointNumber, labelPrefix); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + throw new ConverterCreationException("Error creating converter for cluster " + cluster.id, e); + } + } + + public static class ConverterCreationException extends Exception { + private static final long serialVersionUID = 1L; + + public ConverterCreationException(String message, Throwable cause) { + super(message, cause); + } + + public ConverterCreationException(String message) { + super(message); + } + } + + public static class NoConverterFoundException extends Exception { + private static final long serialVersionUID = 1L; + + public NoConverterFoundException(String message) { + super(message); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java new file mode 100644 index 00000000000..4d69f5fa56c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverter.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_DOORLOCK_STATE; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_DOORLOCK_STATE; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DoorLockCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link DoorLockCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class DoorLockConverter extends GenericConverter { + + public DoorLockConverter(DoorLockCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_DOORLOCK_STATE), CoreItemFactory.SWITCH) + .withType(CHANNEL_DOORLOCK_STATE).build(); + + return Collections.singletonMap(channel, null); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof OnOffType onOffType) { + ClusterCommand doorLockCommand = onOffType == OnOffType.ON ? DoorLockCluster.lockDoor(null) + : DoorLockCluster.unlockDoor(null); + handler.sendClusterCommand(endpointNumber, DoorLockCluster.CLUSTER_NAME, doorLockCommand); + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case "lockState": + if (message.value instanceof DoorLockCluster.LockStateEnum lockState) { + updateState(CHANNEL_ID_DOORLOCK_STATE, + lockState == DoorLockCluster.LockStateEnum.LOCKED ? OnOffType.ON : OnOffType.OFF); + } + default: + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_DOORLOCK_STATE, + initializingCluster.lockState == DoorLockCluster.LockStateEnum.LOCKED ? OnOffType.ON : OnOffType.OFF); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalEnergyMeasurementConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalEnergyMeasurementConverter.java new file mode 100644 index 00000000000..9b2312e4eb2 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalEnergyMeasurementConverter.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import javax.measure.quantity.Energy; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ElectricalEnergyMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * A converter for translating {@link ElectricalEnergyMeasurementCluster} events and attributes to openHAB channels and + * back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ElectricalEnergyMeasurementConverter extends GenericConverter { + + public ElectricalEnergyMeasurementConverter(ElectricalEnergyMeasurementCluster cluster, + MatterBaseThingHandler handler, int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Map map = new HashMap<>(); + if (initializingCluster.featureMap.cumulativeEnergy) { + if (initializingCluster.featureMap.exportedEnergy) { + Channel exportedEnergyChannel = ChannelBuilder.create( + new ChannelUID(channelGroupUID, + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED_ENERGY), + "Number:Energy").withType(CHANNEL_ELECTRICALENERGYMEASUREMENT_ENERGYMEASUREMENT_ENERGY).build(); + map.put(exportedEnergyChannel, null); + } + + if (initializingCluster.featureMap.importedEnergy) { + Channel importedEnergyChannel = ChannelBuilder.create( + new ChannelUID(channelGroupUID, + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED_ENERGY), + "Number:Energy").withType(CHANNEL_ELECTRICALENERGYMEASUREMENT_ENERGYMEASUREMENT_ENERGY).build(); + map.put(importedEnergyChannel, null); + } + } + if (initializingCluster.featureMap.periodicEnergy) { + if (initializingCluster.featureMap.exportedEnergy) { + Channel exportedEnergyChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED_ENERGY), "Number:Energy") + .withType(CHANNEL_ELECTRICALENERGYMEASUREMENT_ENERGYMEASUREMENT_ENERGY).build(); + map.put(exportedEnergyChannel, null); + } + if (initializingCluster.featureMap.importedEnergy) { + Channel importedEnergyChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, + CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED_ENERGY), "Number:Energy") + .withType(CHANNEL_ELECTRICALENERGYMEASUREMENT_ENERGYMEASUREMENT_ENERGY).build(); + map.put(importedEnergyChannel, null); + } + } + return map; + } + + @Override + public void onEvent(AttributeChangedMessage message) { + if (message.value instanceof ElectricalEnergyMeasurementCluster.EnergyMeasurementStruct energyMeasurement) { + switch (message.path.attributeName) { + case ElectricalEnergyMeasurementCluster.ATTRIBUTE_CUMULATIVE_ENERGY_IMPORTED: { + if (energyMeasurement.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED_ENERGY, + activePowerToWatts(energyMeasurement.energy)); + } + } + break; + case ElectricalEnergyMeasurementCluster.ATTRIBUTE_CUMULATIVE_ENERGY_EXPORTED: { + if (energyMeasurement.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED_ENERGY, + activePowerToWatts(energyMeasurement.energy)); + } + } + break; + case ElectricalEnergyMeasurementCluster.ATTRIBUTE_PERIODIC_ENERGY_IMPORTED: { + if (energyMeasurement.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED_ENERGY, + activePowerToWatts(energyMeasurement.energy)); + } + } + break; + case ElectricalEnergyMeasurementCluster.ATTRIBUTE_PERIODIC_ENERGY_EXPORTED: { + if (energyMeasurement.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED_ENERGY, + activePowerToWatts(energyMeasurement.energy)); + } + } + break; + } + } + super.onEvent(message); + } + + @Override + public void initState() { + if (initializingCluster.cumulativeEnergyImported != null + && initializingCluster.cumulativeEnergyImported.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED_ENERGY, + activePowerToWatts(initializingCluster.cumulativeEnergyImported.energy)); + } else { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED_ENERGY, UnDefType.NULL); + } + if (initializingCluster.cumulativeEnergyExported != null + && initializingCluster.cumulativeEnergyExported.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYIMPORTED_ENERGY, + activePowerToWatts(initializingCluster.cumulativeEnergyExported.energy)); + } else { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_CUMULATIVEENERGYEXPORTED_ENERGY, UnDefType.NULL); + } + if (initializingCluster.periodicEnergyImported != null + && initializingCluster.periodicEnergyImported.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED_ENERGY, + activePowerToWatts(initializingCluster.periodicEnergyImported.energy)); + } else { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYIMPORTED_ENERGY, UnDefType.NULL); + } + if (initializingCluster.periodicEnergyExported != null + && initializingCluster.periodicEnergyExported.energy != null) { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED_ENERGY, + activePowerToWatts(initializingCluster.periodicEnergyExported.energy)); + } else { + updateState(CHANNEL_ID_ELECTRICALENERGYMEASUREMENT_PERIODICENERGYEXPORTED_ENERGY, UnDefType.NULL); + } + } + + private QuantityType activePowerToWatts(Number number) { + return new QuantityType(new BigDecimal(number.intValue() / 1000), Units.WATT_HOUR); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalPowerMeasurementConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalPowerMeasurementConverter.java new file mode 100644 index 00000000000..54e0b0fa73c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalPowerMeasurementConverter.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.Map; + +import javax.measure.quantity.ElectricCurrent; +import javax.measure.quantity.ElectricPotential; +import javax.measure.quantity.Power; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ElectricalPowerMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * A converter for translating {@link ElectricalPowerMeasurementCluster} events and attributes to openHAB channels and + * back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ElectricalPowerMeasurementConverter extends GenericConverter { + + public ElectricalPowerMeasurementConverter(ElectricalPowerMeasurementCluster cluster, + MatterBaseThingHandler handler, int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Map map = new HashMap<>(); + // Active Power is mandatory + Channel activePowerChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER), + "Number:Power") + .withType(CHANNEL_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER).build(); + map.put(activePowerChannel, null); + + // optional cluster if not null + if (initializingCluster.activeCurrent != null) { + Channel activeCurrentChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT), + "Number:ElectricCurrent") + .withType(CHANNEL_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT).build(); + map.put(activeCurrentChannel, null); + } + + // optional cluster if not null + if (initializingCluster.voltage != null) { + Channel voltageChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE), + "Number:ElectricPotential") + .withType(CHANNEL_ELECTRICALPOWERMEASUREMENT_VOLTAGE).build(); + map.put(voltageChannel, null); + } + return map; + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case ElectricalPowerMeasurementCluster.ATTRIBUTE_ACTIVE_POWER: + updateState(CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER, + message.value instanceof Number number ? activePowerToWatts(number) : UnDefType.UNDEF); + break; + case ElectricalPowerMeasurementCluster.ATTRIBUTE_ACTIVE_CURRENT: + updateState(CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT, + message.value instanceof Number number ? activeCurrentToAmps(number) : UnDefType.UNDEF); + break; + case ElectricalPowerMeasurementCluster.ATTRIBUTE_VOLTAGE: + updateState(CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE, + message.value instanceof Number number ? milliVoltsToVolts(number) : UnDefType.UNDEF); + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVEPOWER, + initializingCluster.activePower != null ? activePowerToWatts(initializingCluster.activePower) + : UnDefType.NULL); + // optional cluster + if (initializingCluster.activeCurrent != null) { + updateState(CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_ACTIVECURRENT, + initializingCluster.activeCurrent != null ? activeCurrentToAmps(initializingCluster.activeCurrent) + : UnDefType.NULL); + } + if (initializingCluster.voltage != null) { + updateState(CHANNEL_ID_ELECTRICALPOWERMEASUREMENT_VOLTAGE, + initializingCluster.voltage != null ? milliVoltsToVolts(initializingCluster.voltage) + : UnDefType.NULL); + } + } + + private QuantityType activePowerToWatts(Number number) { + return new QuantityType(new BigDecimal(number.intValue() / 1000), Units.WATT); + } + + private QuantityType activeCurrentToAmps(Number number) { + return new QuantityType(new BigDecimal(number.intValue() / 1000), Units.AMPERE); + } + + private QuantityType milliVoltsToVolts(Number number) { + return new QuantityType(new BigDecimal(number.intValue() / 1000), Units.VOLT); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/FanControlConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/FanControlConverter.java new file mode 100644 index 00000000000..f1491838dd0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/FanControlConverter.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.FanControlCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.IncreaseDecreaseType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; +import org.openhab.core.types.UnDefType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A converter for translating {@link FanControlCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class FanControlConverter extends GenericConverter { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + public FanControlConverter(FanControlCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Map channels = new HashMap<>(); + Channel percentChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_FANCONTROL_PERCENT), CoreItemFactory.DIMMER) + .withType(CHANNEL_FANCONTROL_PERCENT).build(); + channels.put(percentChannel, null); + + if (initializingCluster.fanModeSequence != null) { + Channel modeChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_FANCONTROL_MODE), CoreItemFactory.NUMBER) + .withType(CHANNEL_FANCONTROL_MODE).build(); + + List modeOptions = new ArrayList<>(); + + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.OFF.value.toString(), + FanControlCluster.FanModeEnum.OFF.label)); + + switch (initializingCluster.fanModeSequence) { + case OFF_HIGH: + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.HIGH.value.toString(), + FanControlCluster.FanModeEnum.HIGH.label)); + break; + case OFF_HIGH_AUTO: + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.HIGH.value.toString(), + FanControlCluster.FanModeEnum.HIGH.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.AUTO.value.toString(), + FanControlCluster.FanModeEnum.AUTO.label)); + break; + case OFF_LOW_HIGH_AUTO: + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.LOW.value.toString(), + FanControlCluster.FanModeEnum.LOW.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.HIGH.value.toString(), + FanControlCluster.FanModeEnum.HIGH.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.AUTO.value.toString(), + FanControlCluster.FanModeEnum.AUTO.label)); + break; + case OFF_LOW_MED_HIGH_AUTO: + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.LOW.value.toString(), + FanControlCluster.FanModeEnum.LOW.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.MEDIUM.value.toString(), + FanControlCluster.FanModeEnum.MEDIUM.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.HIGH.value.toString(), + FanControlCluster.FanModeEnum.HIGH.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.AUTO.value.toString(), + FanControlCluster.FanModeEnum.AUTO.label)); + + break; + case OFF_LOW_HIGH: + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.LOW.value.toString(), + FanControlCluster.FanModeEnum.LOW.label)); + modeOptions.add(new StateOption(FanControlCluster.FanModeEnum.HIGH.value.toString(), + FanControlCluster.FanModeEnum.HIGH.label)); + break; + default: + break; + } + + StateDescription stateDescriptionMode = StateDescriptionFragmentBuilder.create().withPattern("%d") + .withOptions(modeOptions).build().toStateDescription(); + + channels.put(modeChannel, stateDescriptionMode); + } + return channels; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (channelUID.getIdWithoutGroup().equals(CHANNEL_ID_FANCONTROL_PERCENT)) { + if (command instanceof IncreaseDecreaseType increaseDecreaseType) { + switch (increaseDecreaseType) { + case INCREASE: + moveCommand(FanControlCluster.step(FanControlCluster.StepDirectionEnum.INCREASE, false, false)); + break; + case DECREASE: + moveCommand(FanControlCluster.step(FanControlCluster.StepDirectionEnum.DECREASE, false, true)); + break; + default: + break; + } + } else if (command instanceof PercentType percentType) { + handler.writeAttribute(endpointNumber, FanControlCluster.CLUSTER_NAME, + FanControlCluster.ATTRIBUTE_PERCENT_SETTING, percentType.toString()); + } + } + if (channelUID.getIdWithoutGroup().equals(CHANNEL_ID_FANCONTROL_MODE)) { + if (command instanceof DecimalType decimalType) { + handler.writeAttribute(endpointNumber, FanControlCluster.CLUSTER_NAME, + FanControlCluster.ATTRIBUTE_FAN_MODE, decimalType.toString()); + } + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case FanControlCluster.ATTRIBUTE_FAN_MODE: + if (message.value instanceof FanControlCluster.FanModeEnum fanMode) { + updateState(CHANNEL_ID_FANCONTROL_MODE, new DecimalType(fanMode.value)); + } + break; + case FanControlCluster.ATTRIBUTE_PERCENT_SETTING: + if (message.value instanceof Number percentSetting) { + updateState(CHANNEL_ID_FANCONTROL_PERCENT, new PercentType(percentSetting.intValue())); + } + break; + default: + logger.debug("Unknown attribute {}", message.path.attributeName); + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_FANCONTROL_MODE, + initializingCluster.fanMode != null ? new DecimalType(initializingCluster.fanMode.value) + : UnDefType.NULL); + updateState(CHANNEL_ID_FANCONTROL_PERCENT, + initializingCluster.percentSetting != null ? new PercentType(initializingCluster.percentSetting) + : UnDefType.NULL); + } + + private void moveCommand(ClusterCommand command) { + handler.sendClusterCommand(endpointNumber, FanControlCluster.CLUSTER_NAME, command); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/GenericConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/GenericConverter.java new file mode 100644 index 00000000000..3ecb6fa60b1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/GenericConverter.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.AttributeListener; +import org.openhab.binding.matter.internal.client.EventTriggeredListener; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.State; +import org.openhab.core.types.StateDescription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A generic abstract converter for translating Matter clusters to openHAB channels. + * + * Converters are responsible for converting Matter cluster commands and attributes into openHAB commands and vice + * versa. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public abstract class GenericConverter implements AttributeListener, EventTriggeredListener { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + // This cluster is used for initializing the converter, but is not kept updated as values change over time. + protected T initializingCluster; + protected MatterBaseThingHandler handler; + protected int endpointNumber; + protected String labelPrefix; + // used to REFRESH channels + protected ConcurrentHashMap stateCache = new ConcurrentHashMap<>(); + + public GenericConverter(T cluster, MatterBaseThingHandler handler, int endpointNumber, String labelPrefix) { + this.initializingCluster = cluster; + this.handler = handler; + this.endpointNumber = endpointNumber; + this.labelPrefix = labelPrefix; + } + + public abstract Map createChannels(ChannelGroupUID channelGroupUID); + + /** + * Updates all the channel states of a cluster + */ + public abstract void initState(); + + /** + * Handles a openHAB channel command + */ + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof RefreshType) { + stateCache.forEach((channelId, state) -> handler.updateState(endpointNumber, channelId, state)); + } + } + + /** + * This method is designed to be optionally overridden in subclasses when a cluster need to poll for one or more + * attributes + */ + public void pollCluster() { + // add polling logic here in subclasses + } + + @Override + public void onEvent(AttributeChangedMessage message) { + } + + @Override + public void onEvent(EventTriggeredMessage message) { + } + + public T getInitializingCluster() { + return initializingCluster; + } + + public final void updateState(String channelId, State state) { + handler.updateState(endpointNumber, channelId, state); + stateCache.put(channelId, state); + } + + public final void triggerChannel(String channelId, String event) { + handler.triggerChannel(endpointNumber, channelId, event); + } + + protected String formatLabel(String channelLabel) { + if (labelPrefix.trim().length() > 0) { + return labelPrefix.trim() + " - " + channelLabel; + } + return channelLabel; + } + + protected void updateThingAttributeProperty(String attributeName, @Nullable Object value) { + handler.updateClusterAttributeProperty(initializingCluster.name, attributeName, value); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/IlluminanceMeasurementConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/IlluminanceMeasurementConverter.java new file mode 100644 index 00000000000..fe9cc78f341 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/IlluminanceMeasurementConverter.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.Collections; +import java.util.Map; + +import javax.measure.quantity.Illuminance; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.IlluminanceMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * A converter for translating {@link IlluminanceMeasurementCluster} events and attributes to openHAB channels and back + * again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class IlluminanceMeasurementConverter extends GenericConverter { + + public IlluminanceMeasurementConverter(IlluminanceMeasurementCluster cluster, MatterBaseThingHandler handler, + int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_ILLUMINANCEMEASURMENT_MEASUREDVALUE), + "Number:Illuminance") + .withType(CHANNEL_ILLUMINANCEMEASURMENT_MEASUREDVALUE).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case IlluminanceMeasurementCluster.ATTRIBUTE_MEASURED_VALUE: + if (message.value instanceof Number number) { + updateState(CHANNEL_ID_ILLUMINANCEMEASURMENT_MEASUREDVALUE, + new QuantityType(number.intValue(), Units.LUX)); + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_ILLUMINANCEMEASURMENT_MEASUREDVALUE, + initializingCluster.measuredValue != null + ? new QuantityType(initializingCluster.measuredValue, Units.LUX) + : UnDefType.NULL); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/LevelControlConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/LevelControlConverter.java new file mode 100644 index 00000000000..c710d6b2820 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/LevelControlConverter.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link LevelControlCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class LevelControlConverter extends GenericConverter { + private static final int UPDATE_DELAY_MS = 200; + private @Nullable ScheduledFuture levelFuture = null; + private ScheduledExecutorService updateScheduler = Executors.newSingleThreadScheduledExecutor(); + + private PercentType lastLevel = new PercentType(0); + private OnOffType lastOnOff = OnOffType.OFF; + private Integer onTransitionTime = 0; + private Integer offTransitionTime = 0; + private Integer defaultMoveRate = 0; + + public LevelControlConverter(LevelControlCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + + if (cluster.onTransitionTime != null) { + onTransitionTime = cluster.onTransitionTime; + } else if (cluster.onOffTransitionTime != null) { + onTransitionTime = cluster.onOffTransitionTime; + } + + if (cluster.offTransitionTime != null) { + offTransitionTime = cluster.offTransitionTime; + } else if (cluster.onOffTransitionTime != null) { + offTransitionTime = cluster.onOffTransitionTime; + } + + if (cluster.defaultMoveRate != null) { + defaultMoveRate = cluster.defaultMoveRate; + } + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_LEVEL_LEVEL), CoreItemFactory.DIMMER) + .withType(CHANNEL_LEVEL_LEVEL).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof OnOffType onOffType) { + // lighting types always have a OnOff cluster + if (initializingCluster.featureMap.lighting) { + ClusterCommand onOffCommand = onOffType == OnOffType.ON ? OnOffCluster.on() : OnOffCluster.off(); + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, onOffCommand); + } else { + ClusterCommand levelCommand = LevelControlCluster.moveToLevelWithOnOff( + onOffType == OnOffType.OFF ? 0 : 100, + onOffType == OnOffType.OFF ? offTransitionTime : onTransitionTime, initializingCluster.options, + initializingCluster.options); + handler.sendClusterCommand(endpointNumber, LevelControlCluster.CLUSTER_NAME, levelCommand); + } + } else if (command instanceof PercentType percentType) { + // Min Level for lighting devices is 1, so send OFF if we get a 0 + if (percentType.equals(PercentType.ZERO) && initializingCluster.featureMap.lighting) { + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, OnOffCluster.off()); + } else { + ClusterCommand levelCommand = LevelControlCluster.moveToLevelWithOnOff( + ValueUtils.percentToLevel(percentType), defaultMoveRate, initializingCluster.options, + initializingCluster.options); + handler.sendClusterCommand(endpointNumber, LevelControlCluster.CLUSTER_NAME, levelCommand); + } + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL: + clearUpdateTimer(); + Integer numberValue = message.value instanceof Number number ? number.intValue() : 0; + lastLevel = ValueUtils.levelToPercent(numberValue); + logger.debug("currentLevel {}", lastLevel); + if (lastOnOff == OnOffType.ON) { + updateState(CHANNEL_ID_LEVEL_LEVEL, lastLevel); + } + break; + case OnOffCluster.ATTRIBUTE_ON_OFF: + logger.debug("onOff {}", message.value); + clearUpdateTimer(); + if (message.value instanceof Boolean booleanValue) { + lastOnOff = OnOffType.from(booleanValue); + if (booleanValue) { + // most ON commands are followed by a currentLevel update, but not always + // We wil wait just a bit before sending the last known level to avoid multiple updates + levelFuture = updateScheduler.schedule(() -> { + updateState(CHANNEL_ID_LEVEL_LEVEL, lastLevel); + }, UPDATE_DELAY_MS, TimeUnit.MILLISECONDS); + } else { + updateState(CHANNEL_ID_LEVEL_LEVEL, OnOffType.OFF); + } + + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + // default to on when not used as part of the lighting type + initState(true); + } + + public void initState(boolean onOff) { + lastOnOff = OnOffType.from(onOff); + lastLevel = ValueUtils.levelToPercent(initializingCluster.currentLevel); + updateState(CHANNEL_ID_LEVEL_LEVEL, onOff ? lastLevel : OnOffType.OFF); + } + + private void clearUpdateTimer() { + ScheduledFuture levelFuture = this.levelFuture; + if (levelFuture != null) { + levelFuture.cancel(true); + this.levelFuture = null; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ModeSelectConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ModeSelectConverter.java new file mode 100644 index 00000000000..d9497dfba06 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ModeSelectConverter.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ModeSelectCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; + +/** + * A converter for translating {@link ModeSelectCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ModeSelectConverter extends GenericConverter { + + public ModeSelectConverter(ModeSelectCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_MODESELECT_MODE), CoreItemFactory.NUMBER) + .withType(CHANNEL_MODESELECT_MODE).withLabel(formatLabel(initializingCluster.description)).build(); + + List modeOptions = new ArrayList<>(); + initializingCluster.supportedModes + .forEach(mode -> modeOptions.add(new StateOption(mode.mode.toString(), mode.label))); + + StateDescription stateDescriptionMode = StateDescriptionFragmentBuilder.create().withPattern("%d") + .withOptions(modeOptions).build().toStateDescription(); + + return Collections.singletonMap(channel, stateDescriptionMode); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof DecimalType decimalType) { + ClusterCommand cc = ModeSelectCluster.changeToMode(decimalType.intValue()); + handler.sendClusterCommand(endpointNumber, ModeSelectCluster.CLUSTER_NAME, cc); + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case ModeSelectCluster.ATTRIBUTE_CURRENT_MODE: + if (message.value instanceof Number number) { + updateState(CHANNEL_ID_MODESELECT_MODE, new DecimalType(number.intValue())); + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_MODESELECT_MODE, new DecimalType(initializingCluster.currentMode)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/OccupancySensingConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/OccupancySensingConverter.java new file mode 100644 index 00000000000..5fb95d52c01 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/OccupancySensingConverter.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OccupancySensingCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link OccupancySensingCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class OccupancySensingConverter extends GenericConverter { + + public OccupancySensingConverter(OccupancySensingCluster cluster, MatterBaseThingHandler handler, + int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_OCCUPANCYSENSING_OCCUPIED), CoreItemFactory.SWITCH) + .withType(CHANNEL_OCCUPANCYSENSING_OCCUPIED).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case OccupancySensingCluster.ATTRIBUTE_OCCUPANCY: + if (message.value instanceof OccupancySensingCluster.OccupancyBitmap occupancyBitmap) { + updateState(CHANNEL_ID_OCCUPANCYSENSING_OCCUPIED, OnOffType.from(occupancyBitmap.occupied)); + break; + } + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_OCCUPANCYSENSING_OCCUPIED, OnOffType.from(initializingCluster.occupancy.occupied)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/OnOffConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/OnOffConverter.java new file mode 100644 index 00000000000..842dc26f5fb --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/OnOffConverter.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_ONOFF_ONOFF; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ONOFF_ONOFF; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link OnOffCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class OnOffConverter extends GenericConverter { + + public OnOffConverter(OnOffCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_ONOFF_ONOFF), CoreItemFactory.SWITCH) + .withType(CHANNEL_ONOFF_ONOFF).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof OnOffType onOffType) { + ClusterCommand onOffCommand = onOffType == OnOffType.ON ? OnOffCluster.on() : OnOffCluster.off(); + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, onOffCommand); + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case OnOffCluster.ATTRIBUTE_ON_OFF: + updateState(CHANNEL_ID_ONOFF_ONOFF, OnOffType.from((Boolean) message.value)); + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_ONOFF_ONOFF, OnOffType.from(Boolean.valueOf(initializingCluster.onOff))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/PowerSourceConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/PowerSourceConverter.java new file mode 100644 index 00000000000..cdc2bab4fb5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/PowerSourceConverter.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.measure.quantity.Dimensionless; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.PowerSourceCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.PowerSourceCluster.BatChargeLevelEnum; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.State; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; +import org.openhab.core.types.UnDefType; + +/** + * A converter for translating {@link PowerSourceCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class PowerSourceConverter extends GenericConverter { + + public PowerSourceConverter(PowerSourceCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Map channels = new HashMap<>(); + if (initializingCluster.featureMap.battery) { + if (initializingCluster.batPercentRemaining != null) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_POWER_BATTERYPERCENT), + "Number:Dimensionless") + .withType(CHANNEL_POWER_BATTERYPERCENT).build(); + channels.put(channel, null); + } + if (initializingCluster.batChargeLevel != null) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_POWER_CHARGELEVEL), CoreItemFactory.NUMBER) + .withType(CHANNEL_POWER_CHARGELEVEL).build(); + List options = new ArrayList<>(); + for (BatChargeLevelEnum mode : BatChargeLevelEnum.values()) { + options.add(new StateOption(mode.value.toString(), mode.label)); + } + StateDescription stateDescription = StateDescriptionFragmentBuilder.create().withPattern("%d") + .withOptions(options).build().toStateDescription(); + channels.put(channel, stateDescription); + } + } + return channels; + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case PowerSourceCluster.ATTRIBUTE_BAT_PERCENT_REMAINING: + if (message.value instanceof Number batPercentRemaining) { + updateState(CHANNEL_ID_POWER_BATTERYPERCENT, convertToPercentage(batPercentRemaining.intValue())); + } + break; + case PowerSourceCluster.ATTRIBUTE_BAT_CHARGE_LEVEL: + if (message.value instanceof BatChargeLevelEnum batChargeLevel) { + updateState(CHANNEL_ID_POWER_CHARGELEVEL, new DecimalType(batChargeLevel.value)); + } + break; + default: + logger.debug("Unknown attribute {}", message.path.attributeName); + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_POWER_BATTERYPERCENT, + initializingCluster.batPercentRemaining != null + ? convertToPercentage(initializingCluster.batPercentRemaining) + : UnDefType.NULL); + updateState(CHANNEL_ID_POWER_CHARGELEVEL, + initializingCluster.batChargeLevel != null ? new DecimalType(initializingCluster.batChargeLevel.value) + : UnDefType.NULL); + } + + /** + * Converts a battery charge value in half-percent units to a percentage (0-100). + * Values are expressed in half percent units, ranging from 0 to 200. + * For example, a value of 48 is equivalent to 24%. + * + * @param halfPercentValue the battery charge value in half-percent units. + * @return the percentage of battery charge (0-100) or UnDefType.UNDEF if the value is null or invalid as a + * QuantityType. + */ + private State convertToPercentage(Integer halfPercentValue) { + if (halfPercentValue < 0 || halfPercentValue > 200) { + return UnDefType.UNDEF; // Indicates that the node is unable to assess the value or invalid input. + } + return new QuantityType(halfPercentValue == 0 ? 0 : halfPercentValue / 2, Units.PERCENT); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/RelativeHumidityMeasurementConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/RelativeHumidityMeasurementConverter.java new file mode 100644 index 00000000000..c2cbe53bb78 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/RelativeHumidityMeasurementConverter.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_HUMIDITYMEASURMENT_MEASUREDVALUE; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_HUMIDITYMEASURMENT_MEASUREDVALUE; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Map; + +import javax.measure.quantity.Dimensionless; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.RelativeHumidityMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link RelativeHumidityMeasurementCluster} events and attributes to openHAB channels and + * back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class RelativeHumidityMeasurementConverter extends GenericConverter { + + public RelativeHumidityMeasurementConverter(RelativeHumidityMeasurementCluster cluster, + MatterBaseThingHandler handler, int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_HUMIDITYMEASURMENT_MEASUREDVALUE), + "Number:Dimensionless") + .withType(CHANNEL_HUMIDITYMEASURMENT_MEASUREDVALUE).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case RelativeHumidityMeasurementCluster.ATTRIBUTE_MEASURED_VALUE: + if (message.value instanceof Number number) { + updateState(CHANNEL_ID_HUMIDITYMEASURMENT_MEASUREDVALUE, humidityToPercent(number)); + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_HUMIDITYMEASURMENT_MEASUREDVALUE, humidityToPercent(initializingCluster.measuredValue)); + } + + private QuantityType humidityToPercent(Number number) { + return new QuantityType<>(BigDecimal.valueOf(number.intValue(), 2), Units.PERCENT); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/SwitchConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/SwitchConverter.java new file mode 100644 index 00000000000..de091bf3796 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/SwitchConverter.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.SwitchCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.TriggerEvent; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.thing.type.ChannelKind; +import org.openhab.core.thing.type.ChannelTypeUID; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; + +import com.google.gson.Gson; + +/** + * A converter for translating {@link SwitchCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class SwitchConverter extends GenericConverter { + private Gson gson = new Gson(); + + public SwitchConverter(SwitchCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + final Map map = new java.util.HashMap<>(); + Set triggerChannelTypes = new HashSet<>(); + // See cluster specification table 1.13.4. Switch Features + if (initializingCluster.featureMap.latchingSwitch) { + triggerChannelTypes.add(CHANNEL_SWITCH_SWITCHLATECHED); + } + if (initializingCluster.featureMap.momentarySwitch) { + triggerChannelTypes.add(CHANNEL_SWITCH_INITIALPRESS); + } + if (initializingCluster.featureMap.momentarySwitchRelease) { + triggerChannelTypes.add(CHANNEL_SWITCH_SHORTRELEASE); + } + if (initializingCluster.featureMap.momentarySwitchLongPress) { + triggerChannelTypes.add(CHANNEL_SWITCH_LONGPRESS); + triggerChannelTypes.add(CHANNEL_SWITCH_LONGRELEASE); + } + if (initializingCluster.featureMap.momentarySwitchMultiPress) { + triggerChannelTypes.add(CHANNEL_SWITCH_MULTIPRESSCOMPLETE); + triggerChannelTypes.add(CHANNEL_SWITCH_MULTIPRESSONGOING); + } + triggerChannelTypes + .forEach(type -> map.put(ChannelBuilder.create(new ChannelUID(channelGroupUID, type.getId()), null) + .withType(type).withKind(ChannelKind.TRIGGER).build(), null)); + + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_SWITCH_SWITCH), CoreItemFactory.NUMBER) + .withType(CHANNEL_SWITCH_SWITCH).build(); + + List options = new ArrayList<>(); + for (int i = 0; i < initializingCluster.numberOfPositions; i++) { + options.add(new StateOption(String.valueOf(i), "Position " + i)); + } + + StateDescription stateDescriptionMode = StateDescriptionFragmentBuilder.create().withPattern("%d") + .withOptions(options).build().toStateDescription(); + + map.put(channel, stateDescriptionMode); + + return map; + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case SwitchCluster.ATTRIBUTE_CURRENT_POSITION: + if (message.value instanceof Number number) { + updateState(CHANNEL_ID_SWITCH_SWITCH, new DecimalType(number.intValue())); + } + break; + } + super.onEvent(message); + } + + @Override + public void onEvent(EventTriggeredMessage message) { + String eventName = message.path.eventName.toLowerCase(); + for (TriggerEvent event : message.events) { + triggerChannel("switch-" + eventName, gson.toJson(event.data)); + } + } + + @Override + public void initState() { + updateState(CHANNEL_ID_SWITCH_SWITCH, new DecimalType(initializingCluster.currentPosition)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/TemperatureMeasurementConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/TemperatureMeasurementConverter.java new file mode 100644 index 00000000000..4b7da685a97 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/TemperatureMeasurementConverter.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_TEMPERATUREMEASURMENT_MEASUREDVALUE; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_TEMPERATUREMEASURMENT_MEASUREDVALUE; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.TemperatureMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link TemperatureMeasurementCluster} events and attributes to openHAB channels and back + * again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class TemperatureMeasurementConverter extends GenericConverter { + + public TemperatureMeasurementConverter(TemperatureMeasurementCluster cluster, MatterBaseThingHandler handler, + int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_TEMPERATUREMEASURMENT_MEASUREDVALUE), + "Number:Temperature") + .withType(CHANNEL_TEMPERATUREMEASURMENT_MEASUREDVALUE).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case TemperatureMeasurementCluster.ATTRIBUTE_MEASURED_VALUE: + if (message.value instanceof Number number) { + updateState(CHANNEL_ID_TEMPERATUREMEASURMENT_MEASUREDVALUE, + ValueUtils.valueToTemperature(number.intValue())); + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_TEMPERATUREMEASURMENT_MEASUREDVALUE, + ValueUtils.valueToTemperature(initializingCluster.measuredValue)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThermostatConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThermostatConverter.java new file mode 100644 index 00000000000..3072e47ba78 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThermostatConverter.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThermostatCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.RefreshType; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.StateDescriptionFragmentBuilder; +import org.openhab.core.types.StateOption; +import org.openhab.core.types.UnDefType; + +/** + * A converter for translating {@link ThermostatCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ThermostatConverter extends GenericConverter { + + public ThermostatConverter(ThermostatCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Map channels = new HashMap<>(); + + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_SYSTEMMODE), CoreItemFactory.NUMBER) + .withType(CHANNEL_THERMOSTAT_SYSTEMMODE).build(); + + List modeOptions = new ArrayList<>(); + + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.OFF.value.toString(), + ThermostatCluster.SystemModeEnum.OFF.label)); + if (initializingCluster.featureMap.autoMode) { + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.AUTO.value.toString(), + ThermostatCluster.SystemModeEnum.AUTO.label)); + } + if (initializingCluster.featureMap.cooling) { + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.COOL.value.toString(), + ThermostatCluster.SystemModeEnum.COOL.label)); + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.PRECOOLING.value.toString(), + ThermostatCluster.SystemModeEnum.PRECOOLING.label)); + } + if (initializingCluster.featureMap.heating) { + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.HEAT.value.toString(), + ThermostatCluster.SystemModeEnum.HEAT.label)); + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.EMERGENCY_HEAT.value.toString(), + ThermostatCluster.SystemModeEnum.EMERGENCY_HEAT.label)); + } + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.FAN_ONLY.value.toString(), + ThermostatCluster.SystemModeEnum.FAN_ONLY.label)); + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.DRY.value.toString(), + ThermostatCluster.SystemModeEnum.DRY.label)); + modeOptions.add(new StateOption(ThermostatCluster.SystemModeEnum.SLEEP.value.toString(), + ThermostatCluster.SystemModeEnum.SLEEP.label)); + + StateDescription stateDescriptionMode = StateDescriptionFragmentBuilder.create().withPattern("%d") + .withOptions(modeOptions).build().toStateDescription(); + channels.put(channel, stateDescriptionMode); + + if (!initializingCluster.featureMap.localTemperatureNotExposed) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE), + "Number:Temperature") + .withType(CHANNEL_THERMOSTAT_LOCALTEMPERATURE).build(); + + channels.put(tempChannel, null); + } + if (initializingCluster.outdoorTemperature != null) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE), + "Number:Temperature") + .withType(CHANNEL_THERMOSTAT_OUTDOORTEMPERATURE).build(); + channels.put(tempChannel, null); + } + if (initializingCluster.featureMap.heating) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATING), + "Number:Temperature") + .withType(CHANNEL_THERMOSTAT_OCCUPIEDHEATING).build(); + StateDescription stateDescription = StateDescriptionFragmentBuilder.create() + .withMinimum( + ValueUtils.valueToTemperature(initializingCluster.absMinHeatSetpointLimit).toBigDecimal()) + .withMaximum( + ValueUtils.valueToTemperature(initializingCluster.absMaxHeatSetpointLimit).toBigDecimal()) + .withStep(BigDecimal.valueOf(1)).withPattern("%.1f %unit%").withReadOnly(false).build() + .toStateDescription(); + channels.put(tempChannel, stateDescription); + } + if (initializingCluster.featureMap.cooling) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLING), + "Number:Temperature") + .withType(CHANNEL_THERMOSTAT_OCCUPIEDCOOLING).build(); + StateDescription stateDescription = StateDescriptionFragmentBuilder.create() + .withMinimum( + ValueUtils.valueToTemperature(initializingCluster.absMinCoolSetpointLimit).toBigDecimal()) + .withMaximum( + ValueUtils.valueToTemperature(initializingCluster.absMaxCoolSetpointLimit).toBigDecimal()) + .withStep(BigDecimal.valueOf(1)).withPattern("%.1f %unit%").withReadOnly(false).build() + .toStateDescription(); + channels.put(tempChannel, stateDescription); + } + if (initializingCluster.featureMap.occupancy) { + if (initializingCluster.featureMap.heating) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATING), + "Number:Temperature") + .withType(CHANNEL_THERMOSTAT_UNOCCUPIEDHEATING).build(); + StateDescription stateDescription = StateDescriptionFragmentBuilder.create() + .withMinimum(ValueUtils.valueToTemperature(initializingCluster.absMinHeatSetpointLimit) + .toBigDecimal()) + .withMaximum(ValueUtils.valueToTemperature(initializingCluster.absMaxHeatSetpointLimit) + .toBigDecimal()) + .withStep(BigDecimal.valueOf(1)).withPattern("%.1f %unit%").withReadOnly(false).build() + .toStateDescription(); + channels.put(tempChannel, stateDescription); + } + if (initializingCluster.featureMap.cooling) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLING), + "Number:Temperature") + .withType(CHANNEL_THERMOSTAT_UNOCCUPIEDCOOLING).build(); + StateDescription stateDescription = StateDescriptionFragmentBuilder.create() + .withMinimum(ValueUtils.valueToTemperature(initializingCluster.absMinCoolSetpointLimit) + .toBigDecimal()) + .withMaximum(ValueUtils.valueToTemperature(initializingCluster.absMaxCoolSetpointLimit) + .toBigDecimal()) + .withStep(BigDecimal.valueOf(1)).withPattern("%.1f %unit%").withReadOnly(false).build() + .toStateDescription(); + channels.put(tempChannel, stateDescription); + } + } + if (initializingCluster.thermostatRunningMode != null) { + Channel tempChannel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_THERMOSTAT_RUNNINGMODE), CoreItemFactory.NUMBER) + .withType(CHANNEL_THERMOSTAT_RUNNINGMODE).build(); + List options = new ArrayList<>(); + options.add(new StateOption(ThermostatCluster.ThermostatRunningModeEnum.OFF.value.toString(), + ThermostatCluster.ThermostatRunningModeEnum.OFF.label)); + options.add(new StateOption(ThermostatCluster.ThermostatRunningModeEnum.HEAT.value.toString(), + ThermostatCluster.ThermostatRunningModeEnum.HEAT.label)); + options.add(new StateOption(ThermostatCluster.ThermostatRunningModeEnum.COOL.value.toString(), + ThermostatCluster.ThermostatRunningModeEnum.COOL.label)); + StateDescription stateDescription = StateDescriptionFragmentBuilder.create().withOptions(options).build() + .toStateDescription(); + channels.put(tempChannel, stateDescription); + } + + return channels; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (!(command instanceof RefreshType)) { + String id = channelUID.getIdWithoutGroup(); + if (id.equals(CHANNEL_ID_THERMOSTAT_SYSTEMMODE)) { + handler.writeAttribute(endpointNumber, ThermostatCluster.CLUSTER_NAME, "systemMode", + command.toString()); + return; + } + if (id.equals(CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATING)) { + handler.writeAttribute(endpointNumber, ThermostatCluster.CLUSTER_NAME, "occupiedHeatingSetpoint", + String.valueOf(ValueUtils.temperatureToValue(command))); + return; + } + if (id.equals(CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLING)) { + handler.writeAttribute(endpointNumber, ThermostatCluster.CLUSTER_NAME, "occupiedCoolingSetpoint", + String.valueOf(ValueUtils.temperatureToValue(command))); + return; + } + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + logger.debug("OnEvent: {}", message.path.attributeName); + Integer numberValue = message.value instanceof Number number ? number.intValue() : 0; + switch (message.path.attributeName) { + case ThermostatCluster.ATTRIBUTE_SYSTEM_MODE: + if (message.value instanceof ThermostatCluster.SystemModeEnum systemModeEnum) { + updateState(CHANNEL_ID_THERMOSTAT_SYSTEMMODE, new DecimalType(systemModeEnum.value)); + } + break; + case ThermostatCluster.ATTRIBUTE_OCCUPIED_HEATING_SETPOINT: + updateState(CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATING, ValueUtils.valueToTemperature(numberValue)); + break; + case ThermostatCluster.ATTRIBUTE_OCCUPIED_COOLING_SETPOINT: + updateState(CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLING, ValueUtils.valueToTemperature(numberValue)); + break; + case ThermostatCluster.ATTRIBUTE_UNOCCUPIED_HEATING_SETPOINT: + updateState(CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATING, ValueUtils.valueToTemperature(numberValue)); + break; + case ThermostatCluster.ATTRIBUTE_UNOCCUPIED_COOLING_SETPOINT: + updateState(CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLING, ValueUtils.valueToTemperature(numberValue)); + break; + case ThermostatCluster.ATTRIBUTE_LOCAL_TEMPERATURE: + updateState(CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE, ValueUtils.valueToTemperature(numberValue)); + break; + case ThermostatCluster.ATTRIBUTE_OUTDOOR_TEMPERATURE: + updateState(CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE, ValueUtils.valueToTemperature(numberValue)); + break; + case ThermostatCluster.ATTRIBUTE_THERMOSTAT_RUNNING_MODE: + updateState(CHANNEL_ID_THERMOSTAT_RUNNINGMODE, new DecimalType(numberValue)); + break; + default: + logger.debug("Unknown attribute {}", message.path.attributeName); + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_THERMOSTAT_LOCALTEMPERATURE, + initializingCluster.localTemperature != null + ? ValueUtils.valueToTemperature(initializingCluster.localTemperature) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_OUTDOORTEMPERATURE, + initializingCluster.outdoorTemperature != null + ? ValueUtils.valueToTemperature(initializingCluster.outdoorTemperature) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_SYSTEMMODE, + initializingCluster.systemMode != null ? new DecimalType(initializingCluster.systemMode.value) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_OCCUPIEDHEATING, + initializingCluster.occupiedHeatingSetpoint != null + ? ValueUtils.valueToTemperature(initializingCluster.occupiedHeatingSetpoint) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_OCCUPIEDCOOLING, + initializingCluster.occupiedCoolingSetpoint != null + ? ValueUtils.valueToTemperature(initializingCluster.occupiedCoolingSetpoint) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_UNOCCUPIEDHEATING, + initializingCluster.unoccupiedHeatingSetpoint != null + ? ValueUtils.valueToTemperature(initializingCluster.unoccupiedHeatingSetpoint) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_UNOCCUPIEDCOOLING, + initializingCluster.unoccupiedCoolingSetpoint != null + ? ValueUtils.valueToTemperature(initializingCluster.unoccupiedCoolingSetpoint) + : UnDefType.NULL); + updateState(CHANNEL_ID_THERMOSTAT_RUNNINGMODE, + initializingCluster.thermostatRunningMode != null + ? new DecimalType(initializingCluster.thermostatRunningMode.value) + : UnDefType.NULL); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadBorderRouterManagementConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadBorderRouterManagementConverter.java new file mode 100644 index 00000000000..2b936197f71 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadBorderRouterManagementConverter.java @@ -0,0 +1,554 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterBindingConstants; +import org.openhab.binding.matter.internal.actions.MatterOTBRActions; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.GeneralCommissioningCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThreadBorderRouterManagementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.binding.matter.internal.util.ThreadDataset; +import org.openhab.core.config.core.ConfigDescriptionBuilder; +import org.openhab.core.config.core.ConfigDescriptionParameter; +import org.openhab.core.config.core.ConfigDescriptionParameter.Type; +import org.openhab.core.config.core.ConfigDescriptionParameterBuilder; +import org.openhab.core.config.core.ConfigDescriptionParameterGroup; +import org.openhab.core.config.core.ConfigDescriptionParameterGroupBuilder; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link ThreadBorderRouterManagementCluster} events and attributes to openHAB channels and + * back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ThreadBorderRouterManagementConverter extends GenericConverter { + private static final int FAIL_SAFE_TIMEOUT_SECONDS = 30; + private static final int ROOT_ENDPOINT = 0; + private static final String ACTIVE_DATASET = "activeDataset"; + private static final String PENDING_DATASET = "pendingDataset"; + private final AtomicBoolean activeDatasetPending = new AtomicBoolean(false); + private final AtomicBoolean pendingDatasetPending = new AtomicBoolean(false); + + public ThreadBorderRouterManagementConverter(ThreadBorderRouterManagementCluster cluster, + MatterBaseThingHandler handler, int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + handler.registerService(MatterOTBRActions.class); + updateConfigDescription(); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + return Map.of(); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + logger.debug("onEvent: {} {}", message.path.attributeName, message.value); + updateThingAttributeProperty(message.path.attributeName, message.value); + switch (message.path.attributeName) { + case ThreadBorderRouterManagementCluster.ATTRIBUTE_ACTIVE_DATASET_TIMESTAMP: + if (message.value == null || message.value.equals("null")) { + updateThingAttributeProperty(ACTIVE_DATASET, null); + } else { + getActiveDataset().thenAccept(result -> { + updateThingAttributeProperty(ACTIVE_DATASET, result); + updateThreadConfiguration(result); + }).exceptionally(e -> { + logger.debug("Error getting active dataset after timestamp update", e); + updateThingAttributeProperty(ACTIVE_DATASET, null); + throw new CompletionException(e); + }); + } + break; + case ThreadBorderRouterManagementCluster.ATTRIBUTE_PENDING_DATASET_TIMESTAMP: + if (message.value == null || message.value.equals("null")) { + updateThingAttributeProperty(PENDING_DATASET, null); + // If the pending dataset timestamp is null, we need to get the active dataset as the pending moved + // to active + getActiveDataset().thenAccept(result -> { + updateThingAttributeProperty(ACTIVE_DATASET, result); + updateThreadConfiguration(result); + }).exceptionally(e -> { + logger.debug("Error getting active dataset after timestamp update", e); + updateThingAttributeProperty(ACTIVE_DATASET, null); + throw new CompletionException(e); + }); + } else { + getPendingDataset().thenAccept(result -> { + updateThingAttributeProperty(PENDING_DATASET, result); + }).exceptionally(e -> { + logger.debug("Error getting pending dataset after timestamp update", e); + updateThingAttributeProperty(PENDING_DATASET, null); + throw new CompletionException(e); + }); + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateThingAttributeProperty(ThreadBorderRouterManagementCluster.ATTRIBUTE_BORDER_AGENT_ID, + initializingCluster.borderAgentId); + updateThingAttributeProperty(ThreadBorderRouterManagementCluster.ATTRIBUTE_BORDER_ROUTER_NAME, + initializingCluster.borderRouterName); + updateThingAttributeProperty(ThreadBorderRouterManagementCluster.ATTRIBUTE_THREAD_VERSION, + initializingCluster.threadVersion); + updateThingAttributeProperty(ThreadBorderRouterManagementCluster.ATTRIBUTE_INTERFACE_ENABLED, + initializingCluster.interfaceEnabled); + updateThingAttributeProperty(ThreadBorderRouterManagementCluster.ATTRIBUTE_ACTIVE_DATASET_TIMESTAMP, + initializingCluster.activeDatasetTimestamp); + updateThingAttributeProperty(ThreadBorderRouterManagementCluster.ATTRIBUTE_PENDING_DATASET_TIMESTAMP, + initializingCluster.pendingDatasetTimestamp); + if (initializingCluster.activeDatasetTimestamp != null) { + getActiveDataset().thenAccept(result -> { + updateThingAttributeProperty(ACTIVE_DATASET, result); + updateThreadConfiguration(result); + }).exceptionally(e -> { + logger.debug("Error getting active dataset during init", e); + updateThingAttributeProperty(ACTIVE_DATASET, null); + return null; + }); + } else { + updateThingAttributeProperty(ACTIVE_DATASET, null); + } + if (initializingCluster.pendingDatasetTimestamp != null) { + getPendingDataset().thenAccept(result -> { + updateThingAttributeProperty(PENDING_DATASET, result); + }).exceptionally(e -> { + logger.debug("Error getting pending dataset during init", e); + updateThingAttributeProperty(PENDING_DATASET, null); + return null; + }); + } else { + updateThingAttributeProperty(PENDING_DATASET, null); + } + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + public synchronized CompletableFuture getActiveDataset() { + if (activeDatasetPending.get()) { + return CompletableFuture.failedFuture(new IllegalStateException("Active dataset request already pending")); + } + activeDatasetPending.set(true); + return handler.sendClusterCommand(endpointNumber, ThreadBorderRouterManagementCluster.CLUSTER_NAME, + ThreadBorderRouterManagementCluster.getActiveDatasetRequest()).thenApply(result -> { + return result.getAsJsonObject().get("dataset").getAsString(); + }).exceptionally(e -> { + logger.debug("Error getting active dataset", e); + throw new CompletionException(e); + }).whenComplete((r, e) -> { + activeDatasetPending.set(false); + }); + } + + public synchronized CompletableFuture getPendingDataset() { + if (pendingDatasetPending.get()) { + return CompletableFuture.failedFuture(new IllegalStateException("Pending dataset request already pending")); + } + pendingDatasetPending.set(true); + return handler.sendClusterCommand(endpointNumber, ThreadBorderRouterManagementCluster.CLUSTER_NAME, + ThreadBorderRouterManagementCluster.getPendingDatasetRequest()).thenApply(result -> { + return result.getAsJsonObject().get("dataset").getAsString(); + }).exceptionally(e -> { + logger.debug("Error getting pending dataset", e); + throw new CompletionException(e); + }).whenComplete((r, e) -> { + pendingDatasetPending.set(false); + }); + } + + public synchronized CompletableFuture setActiveDataset(String dataset) { + if (activeDatasetPending.get()) { + return CompletableFuture.failedFuture(new IllegalStateException("Active dataset request already pending")); + } + activeDatasetPending.set(true); + + return handler + .sendClusterCommand(ROOT_ENDPOINT, GeneralCommissioningCluster.CLUSTER_NAME, + GeneralCommissioningCluster.armFailSafe(FAIL_SAFE_TIMEOUT_SECONDS, BigInteger.ZERO)) + .thenCompose(result -> { + // When the device acknowledges the arm fail safe we can set the active dataset + return handler.sendClusterCommand(endpointNumber, ThreadBorderRouterManagementCluster.CLUSTER_NAME, + ThreadBorderRouterManagementCluster + .setActiveDatasetRequest(new BaseCluster.OctetString(dataset), null)); + }).thenCompose(result2 -> { + logger.debug("Active dataset set"); + // When the device acknowledges the active dataset we can complete arm fail safe + return handler.sendClusterCommand(ROOT_ENDPOINT, GeneralCommissioningCluster.CLUSTER_NAME, + GeneralCommissioningCluster.commissioningComplete()); + }). thenApply(result3 -> { + logger.debug("operation dataset set confirmed"); + return Void.TYPE.cast(null); + }).exceptionally(e -> { + logger.error("Error in setActiveDataset", e); + throw new CompletionException(e); + }).whenComplete((r, e) -> { + activeDatasetPending.set(false); + }); + } + + public synchronized CompletableFuture setPendingDataset(String dataset) { + if (pendingDatasetPending.get()) { + return CompletableFuture.failedFuture(new IllegalStateException("Pending dataset request already pending")); + } + pendingDatasetPending.set(true); + + return handler + .sendClusterCommand(endpointNumber, ThreadBorderRouterManagementCluster.CLUSTER_NAME, + ThreadBorderRouterManagementCluster + .setPendingDatasetRequest(new BaseCluster.OctetString(dataset))) + . thenApply(result -> { + logger.debug("Pending dataset set"); + return Void.TYPE.cast(null); + }).exceptionally(e -> { + logger.error("Error setting pending dataset", e); + throw new CompletionException(e); + }).whenComplete((r, e) -> { + pendingDatasetPending.set(false); + }); + } + + private void updateConfigDescription() { + List params = new ArrayList<>(); + List groups = new ArrayList<>(); + + groups.add(ConfigDescriptionParameterGroupBuilder + .create(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE) + .withLabel(handler + .getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_BORDER_ROUTER_OPERATIONAL_DATASET)) + .withDescription(handler + .getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_BORDER_ROUTER_OPERATIONAL_DATASET)) + .build()); + + ConfigDescriptionParameterBuilder builder = ConfigDescriptionParameterBuilder + .create(MatterBindingConstants.CONFIG_THREAD_CHANNEL, Type.INTEGER); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_NETWORK_CHANNEL_NUMBER)); + builder.withDefault("0"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_NETWORK_CHANNEL_NUMBER)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_ALLOWED_CHANNELS, + Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_NETWORK_ALLOWED_CHANNELS)); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_NETWORK_ALLOWED_CHANNELS)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_EXTENDED_PAN_ID, + Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_EXTENDED_PAN_ID)); + builder.withDefault(""); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_EXTENDED_PAN_ID)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_MESH_LOCAL_PREFIX, + Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_MESH_LOCAL_PREFIX)); + builder.withDefault("0"); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_MESH_LOCAL_PREFIX)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_NETWORK_NAME, + Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_NETWORK_NAME)); + builder.withDefault("0"); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_NETWORK_NAME)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_NETWORK_KEY, Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_NETWORK_KEY)); + builder.withDefault(""); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_NETWORK_KEY)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_PAN_ID, Type.INTEGER); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_PAN_ID)); + builder.withDefault("0"); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_PAN_ID)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_PSKC, Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_PSKC)); + builder.withDefault("0"); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_PSKC)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder + .create(MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_SECONDS, Type.TEXT); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_ACTIVE_TIMESTAMP_SECONDS)); + builder.withDefault("1"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_ACTIVE_TIMESTAMP_SECONDS)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_TICKS, + Type.INTEGER); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_ACTIVE_TIMESTAMP_TICKS)); + builder.withDefault("0"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_ACTIVE_TIMESTAMP_TICKS)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder + .create(MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_AUTHORITATIVE, Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_ACTIVE_TIMESTAMP_IS_AUTHORITATIVE)); + builder.withDefault("false"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_ACTIVE_TIMESTAMP_IS_AUTHORITATIVE)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_THREAD_BORDER_ROUTER_CORE); + builder.withAdvanced(true); + params.add(builder.build()); + + groups.add(ConfigDescriptionParameterGroupBuilder.create(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY) + .withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_THREAD_DATASET_SECURITY_POLICY)) + .withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_THREAD_DATASET_SECURITY_POLICY)) + .withAdvanced(true).build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_ROTATION_TIME, + Type.INTEGER); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_ROTATION_TIME)); + builder.withDefault("672"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_ROTATION_TIME)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_OBTAIN_NETWORK_KEY, + Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_OBTAIN_NETWORK_KEY)); + builder.withDefault("true"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_OBTAIN_NETWORK_KEY)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_NATIVE_COMMISSIONING, + Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_NATIVE_COMMISSIONING)); + builder.withDefault("true"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_NATIVE_COMMISSIONING)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_ROUTERS, Type.BOOLEAN); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_ROUTERS)); + builder.withDefault("true"); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_ROUTERS)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_EXTERNAL_COMMISSIONING, + Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_EXTERNAL_COMMISSIONING)); + builder.withDefault("true"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_EXTERNAL_COMMISSIONING)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder + .create(MatterBindingConstants.CONFIG_THREAD_COMMERCIAL_COMMISSIONING, Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_COMMERCIAL_COMMISSIONING)); + builder.withDefault("false"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_COMMERCIAL_COMMISSIONING)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_AUTONOMOUS_ENROLLMENT, + Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_AUTONOMOUS_ENROLLMENT)); + builder.withDefault("true"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_AUTONOMOUS_ENROLLMENT)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder + .create(MatterBindingConstants.CONFIG_THREAD_NETWORK_KEY_PROVISIONING, Type.BOOLEAN); + builder.withLabel( + handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_NETWORK_KEY_PROVISIONING)); + builder.withDefault("true"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_NETWORK_KEY_PROVISIONING)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_TOBLE_LINK, + Type.BOOLEAN); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_TO_BLE_LINK)); + builder.withDefault("true"); + builder.withDescription(handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_TO_BLE_LINK)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + builder = ConfigDescriptionParameterBuilder.create(MatterBindingConstants.CONFIG_THREAD_NON_CCM_ROUTERS, + Type.BOOLEAN); + builder.withLabel(handler.getTranslation(MatterBindingConstants.CONFIG_LABEL_SECURITY_POLICY_NON_CCM_ROUTERS)); + builder.withDefault("false"); + builder.withDescription( + handler.getTranslation(MatterBindingConstants.CONFIG_DESC_SECURITY_POLICY_NON_CCM_ROUTERS)); + builder.withGroupName(MatterBindingConstants.CONFIG_GROUP_SECURITY_POLICY); + builder.withAdvanced(true); + params.add(builder.build()); + + handler.addConfigDescription((ConfigDescriptionBuilder.create(handler.getConfigDescriptionURI()) + .withParameters(params).withParameterGroups(groups).build())); + } + + public void updateThreadConfiguration(String hexDataset) { + ThreadDataset dataset = ThreadDataset.fromHex(hexDataset); + + Map entries = new HashMap<>(); + dataset.getChannel().ifPresent(c -> entries.put(MatterBindingConstants.CONFIG_THREAD_CHANNEL, c)); + dataset.getChannelSet().ifPresent(cm -> entries.put(MatterBindingConstants.CONFIG_THREAD_ALLOWED_CHANNELS, + cm.stream().map(Object::toString).collect(Collectors.joining(",")))); + dataset.getExtPanIdHex().ifPresent(ep -> entries.put(MatterBindingConstants.CONFIG_THREAD_EXTENDED_PAN_ID, ep)); + dataset.getMeshLocalPrefixFormatted() + .ifPresent(mlp -> entries.put(MatterBindingConstants.CONFIG_THREAD_MESH_LOCAL_PREFIX, mlp)); + dataset.getNetworkName().ifPresent(nn -> entries.put(MatterBindingConstants.CONFIG_THREAD_NETWORK_NAME, nn)); + dataset.getNetworkKeyHex().ifPresent(nk -> entries.put(MatterBindingConstants.CONFIG_THREAD_NETWORK_KEY, nk)); + dataset.getPanId().ifPresent(pid -> entries.put(MatterBindingConstants.CONFIG_THREAD_PAN_ID, pid)); + dataset.getPskcHex().ifPresent(pskc -> entries.put(MatterBindingConstants.CONFIG_THREAD_PSKC, pskc)); + dataset.getSecurityPolicyRotation() + .ifPresent(rt -> entries.put(MatterBindingConstants.CONFIG_THREAD_ROTATION_TIME, rt)); + dataset.getActiveTimestampObject().ifPresent(ts -> { + entries.put(MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_SECONDS, ts.getSeconds()); + entries.put(MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_TICKS, ts.getTicks()); + entries.put(MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_AUTHORITATIVE, ts.isAuthoritative()); + }); + entries.put(MatterBindingConstants.CONFIG_THREAD_OBTAIN_NETWORK_KEY, dataset.isObtainNetworkKey()); + entries.put(MatterBindingConstants.CONFIG_THREAD_NATIVE_COMMISSIONING, dataset.isNativeCommissioning()); + entries.put(MatterBindingConstants.CONFIG_THREAD_ROUTERS, dataset.isRoutersEnabled()); + entries.put(MatterBindingConstants.CONFIG_THREAD_EXTERNAL_COMMISSIONING, dataset.isExternalCommissioning()); + entries.put(MatterBindingConstants.CONFIG_THREAD_COMMERCIAL_COMMISSIONING, dataset.isCommercialCommissioning()); + entries.put(MatterBindingConstants.CONFIG_THREAD_AUTONOMOUS_ENROLLMENT, dataset.isAutonomousEnrollment()); + entries.put(MatterBindingConstants.CONFIG_THREAD_NETWORK_KEY_PROVISIONING, dataset.isNetworkKeyProvisioning()); + entries.put(MatterBindingConstants.CONFIG_THREAD_TOBLE_LINK, dataset.isToBleLink()); + entries.put(MatterBindingConstants.CONFIG_THREAD_NON_CCM_ROUTERS, dataset.isNonCcmRouters()); + handler.updateConfiguration(entries); + logger.debug("Updated thread configuration: {}", dataset.toJson()); + } + + public ThreadDataset datasetFromConfiguration() { + Configuration config = handler.getThing().getConfiguration(); + ThreadDataset dataset = new ThreadDataset(); + ThreadDataset.ThreadTimestamp ts = new ThreadDataset.ThreadTimestamp(1, 0, false); + config.getProperties().forEach((key, value) -> { + switch (key) { + case MatterBindingConstants.CONFIG_THREAD_CHANNEL -> + dataset.setChannel(new BigDecimal(value.toString()).intValue()); + case MatterBindingConstants.CONFIG_THREAD_ALLOWED_CHANNELS -> + dataset.setChannelSet(new java.util.HashSet<>( + Arrays.stream(value.toString().split(",")).map(Integer::parseInt).toList())); + case MatterBindingConstants.CONFIG_THREAD_EXTENDED_PAN_ID -> dataset.setExtPanId(value.toString()); + case MatterBindingConstants.CONFIG_THREAD_MESH_LOCAL_PREFIX -> + dataset.setMeshLocalPrefixFormatted(value.toString()); + case MatterBindingConstants.CONFIG_THREAD_NETWORK_NAME -> dataset.setNetworkName(value.toString()); + case MatterBindingConstants.CONFIG_THREAD_NETWORK_KEY -> dataset.setNetworkKey(value.toString()); + case MatterBindingConstants.CONFIG_THREAD_PAN_ID -> + dataset.setPanId(new BigDecimal(value.toString()).intValue()); + case MatterBindingConstants.CONFIG_THREAD_PSKC -> dataset.setPskc(value.toString()); + case MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_SECONDS -> + ts.setSeconds(new BigDecimal(value.toString()).longValue()); + case MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_TICKS -> + ts.setTicks(new BigDecimal(value.toString()).intValue()); + case MatterBindingConstants.CONFIG_THREAD_ACTIVE_TIMESTAMP_AUTHORITATIVE -> + ts.setAuthoritative(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_DELAY_TIMER -> + dataset.setDelayTimer(new BigDecimal(value.toString()).longValue()); + case MatterBindingConstants.CONFIG_THREAD_ROTATION_TIME -> + dataset.setSecurityPolicyRotation(new BigDecimal(value.toString()).intValue()); + case MatterBindingConstants.CONFIG_THREAD_OBTAIN_NETWORK_KEY -> + dataset.setObtainNetworkKey(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_NATIVE_COMMISSIONING -> + dataset.setNativeCommissioning(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_ROUTERS -> + dataset.setRoutersEnabled(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_EXTERNAL_COMMISSIONING -> + dataset.setExternalCommissioning(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_COMMERCIAL_COMMISSIONING -> + dataset.setCommercialCommissioning(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_AUTONOMOUS_ENROLLMENT -> + dataset.setAutonomousEnrollment(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_NETWORK_KEY_PROVISIONING -> + dataset.setNetworkKeyProvisioning(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_TOBLE_LINK -> + dataset.setToBleLink(Boolean.parseBoolean(value.toString())); + case MatterBindingConstants.CONFIG_THREAD_NON_CCM_ROUTERS -> + dataset.setNonCcmRouters(Boolean.parseBoolean(value.toString())); + default -> logger.debug("Unknown configuration property: {}", key); + } + dataset.setActiveTimestamp(ts); + }); + return dataset; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadNetworkDiagnosticsConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadNetworkDiagnosticsConverter.java new file mode 100644 index 00000000000..358f7daa77e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadNetworkDiagnosticsConverter.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThreadNetworkDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +import com.google.gson.Gson; + +/** + * A converter for translating {@link ThreadNetworkDiagnosticsCluster} events and attributes to openHAB channels and + * back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ThreadNetworkDiagnosticsConverter extends GenericConverter { + private Gson gson = new Gson(); + + public ThreadNetworkDiagnosticsConverter(ThreadNetworkDiagnosticsCluster cluster, MatterBaseThingHandler handler, + int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public void pollCluster() { + // read the whole cluster + handler.readCluster(ThreadNetworkDiagnosticsCluster.class, endpointNumber, initializingCluster.id) + .thenAccept(cluster -> { + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_CHANNEL, cluster.channel); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTING_ROLE, + cluster.routingRole); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NETWORK_NAME, + cluster.networkName); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_PAN_ID, cluster.panId); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_EXTENDED_PAN_ID, + cluster.extendedPanId); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_RLOC16, cluster.rloc16); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NEIGHBOR_TABLE, + gson.toJson(cluster.neighborTable)); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTE_TABLE, + gson.toJson(cluster.routeTable)); + }).exceptionally(e -> { + logger.debug("Error polling thread network diagnostics", e); + return null; + }); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + return Collections.emptyMap(); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + updateThingAttributeProperty(message.path.attributeName, message.value); + super.onEvent(message); + } + + @Override + public void initState() { + logger.debug("initState"); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_CHANNEL, initializingCluster.channel); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTING_ROLE, + initializingCluster.routingRole); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NETWORK_NAME, + initializingCluster.networkName); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_PAN_ID, initializingCluster.panId); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_EXTENDED_PAN_ID, + initializingCluster.extendedPanId); + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_RLOC16, initializingCluster.rloc16); + String neighborTable = initializingCluster.neighborTable != null + ? gson.toJson(initializingCluster.neighborTable) + : null; + updateThingAttributeProperty(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NEIGHBOR_TABLE, neighborTable); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/WiFiNetworkDiagnosticsConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/WiFiNetworkDiagnosticsConverter.java new file mode 100644 index 00000000000..283c4d3297d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/WiFiNetworkDiagnosticsConverter.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_WIFINETWORKDIAGNOSTICS_RSSI; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WiFiNetworkDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * A converter for translating {@link WiFiNetworkDiagnosticsCluster} events and attributes to openHAB channels and back + * again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class WiFiNetworkDiagnosticsConverter extends GenericConverter { + + public WiFiNetworkDiagnosticsConverter(WiFiNetworkDiagnosticsCluster cluster, MatterBaseThingHandler handler, + int endpointNumber, String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public void pollCluster() { + // we only need to read a single attribute, not the whole cluster + handler.readAttribute(endpointNumber, initializingCluster.name, WiFiNetworkDiagnosticsCluster.ATTRIBUTE_RSSI) + .thenAccept(rssi -> { + updateState(CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI, + new QuantityType<>(Integer.parseInt(rssi), Units.DECIBEL_MILLIWATTS)); + updateThingAttributeProperty(WiFiNetworkDiagnosticsCluster.ATTRIBUTE_RSSI, rssi); + }).exceptionally(e -> { + logger.debug("Error polling wifi network diagnostics", e); + return null; + }); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI), "Number:Power") + .withType(CHANNEL_WIFINETWORKDIAGNOSTICS_RSSI).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + switch (message.path.attributeName) { + case WiFiNetworkDiagnosticsCluster.ATTRIBUTE_RSSI: + if (message.value instanceof Number number) { + updateState(CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI, + new QuantityType<>(number, Units.DECIBEL_MILLIWATTS)); + updateThingAttributeProperty(WiFiNetworkDiagnosticsCluster.ATTRIBUTE_RSSI, number); + } + break; + } + super.onEvent(message); + } + + @Override + public void initState() { + updateState(CHANNEL_ID_WIFINETWORKDIAGNOSTICS_RSSI, + initializingCluster.rssi != null + ? new QuantityType<>(initializingCluster.rssi, Units.DECIBEL_MILLIWATTS) + : UnDefType.NULL); + updateThingAttributeProperty(WiFiNetworkDiagnosticsCluster.ATTRIBUTE_RSSI, initializingCluster.rssi); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/WindowCoveringConverter.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/WindowCoveringConverter.java new file mode 100644 index 00000000000..b09c9e88dc3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/converter/WindowCoveringConverter.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_ID_WINDOWCOVERING_LIFT; +import static org.openhab.binding.matter.internal.MatterBindingConstants.CHANNEL_WINDOWCOVERING_LIFT; + +import java.util.Collections; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WindowCoveringCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.CoreItemFactory; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.binding.builder.ChannelBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; + +/** + * A converter for translating {@link WindowCoveringCluster} events and attributes to openHAB channels and back again. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class WindowCoveringConverter extends GenericConverter { + + public WindowCoveringConverter(WindowCoveringCluster cluster, MatterBaseThingHandler handler, int endpointNumber, + String labelPrefix) { + super(cluster, handler, endpointNumber, labelPrefix); + } + + @Override + public Map createChannels(ChannelGroupUID channelGroupUID) { + Channel channel = ChannelBuilder + .create(new ChannelUID(channelGroupUID, CHANNEL_ID_WINDOWCOVERING_LIFT), CoreItemFactory.ROLLERSHUTTER) + .withType(CHANNEL_WINDOWCOVERING_LIFT).build(); + return Collections.singletonMap(channel, null); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + if (command instanceof UpDownType upDownType) { + switch (upDownType) { + case UP: + moveCommand(WindowCoveringCluster.upOrOpen()); + break; + case DOWN: + moveCommand(WindowCoveringCluster.downOrClose()); + break; + default: + break; + } + } else if (command instanceof StopMoveType stopMoveType) { + switch (stopMoveType) { + case STOP: + moveCommand(WindowCoveringCluster.stopMotion()); + break; + default: + break; + } + } else if (command instanceof PercentType percentType) { + moveCommand(WindowCoveringCluster.goToLiftPercentage(percentType.intValue())); + } + super.handleCommand(channelUID, command); + } + + @Override + public void onEvent(AttributeChangedMessage message) { + Integer numberValue = message.value instanceof Number number ? number.intValue() : 0; + switch (message.path.attributeName) { + case WindowCoveringCluster.ATTRIBUTE_CURRENT_POSITION_LIFT_PERCENTAGE: + updateState(CHANNEL_ID_WINDOWCOVERING_LIFT, new PercentType(numberValue)); + break; + case WindowCoveringCluster.ATTRIBUTE_CURRENT_POSITION_LIFT_PERCENT100THS: + updateState(CHANNEL_ID_WINDOWCOVERING_LIFT, new PercentType(numberValue / 100)); + break; + default: + logger.debug("Unknown attribute {}", message.path.attributeName); + } + super.onEvent(message); + } + + @Override + public void initState() { + Integer pos = 0; + if (initializingCluster.currentPositionLiftPercentage != null) { + pos = initializingCluster.currentPositionLiftPercentage; + } else if (initializingCluster.currentPositionLiftPercent100ths != null) { + pos = initializingCluster.currentPositionLiftPercent100ths / 100; + } + updateState(CHANNEL_ID_WINDOWCOVERING_LIFT, new PercentType(pos)); + } + + private void moveCommand(ClusterCommand command) { + handler.sendClusterCommand(endpointNumber, WindowCoveringCluster.CLUSTER_NAME, command); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/DeviceType.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/DeviceType.java new file mode 100644 index 00000000000..eb6d84f7656 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/DeviceType.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.types; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.AttributeListener; +import org.openhab.binding.matter.internal.client.EventTriggeredListener; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.controller.devices.converter.ConverterRegistry; +import org.openhab.binding.matter.internal.controller.devices.converter.GenericConverter; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An abstract class that represents a Matter Device type + * + * A Matter Device type is a grouping of clusters that represent a single device, like a thermostat, Light, + * etc. This classification specifies which clusters are mandatory and which are optional for a given device + * type, although devices can have any number or type of Matter clusters. This is suppose to ease client + * development by providing a common interface for interacting with common devices types. + * + * The DeviceType class coordinates sending openHAB commands to Matter clusters and updating openHAB channels + * based on Matter cluster events. Some device types like lighting devices require coordination among their + * clusters, others do not. A DeviceType Class depends on one or more GenericConverter classes to handle the + * conversion of Matter cluster events to openHAB channel updates and openHAB channel commands to Matter cluster + * commands. + * + * Typically, we map a single openHAB channel or item type, like Color, which accepts multiple command types: + * HSB,Percent, and OnOff to multiple Matter clusters, like ColorControl and LevelControl and OnOffControl + * + * Most Device types need little coordination so the default logic (and GenericType instance) will suffice, but + * this can be overridden to provide custom logic for more complex devices (like lighting) + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public abstract class DeviceType implements AttributeListener, EventTriggeredListener { + private final Logger logger = LoggerFactory.getLogger(DeviceType.class); + + protected Integer deviceType; + protected Integer endpointNumber; + protected MatterBaseThingHandler handler; + + protected Map> channelUIDToConverters = new HashMap<>(); + protected Map channelUIDToStateDescription = new HashMap<>(); + protected Map> clusterToConverters = new HashMap<>(); + protected Map allClusters = new HashMap<>(); + + public DeviceType(Integer deviceType, MatterBaseThingHandler handler, Integer endpointNumber) { + this.deviceType = deviceType; + this.handler = handler; + this.endpointNumber = endpointNumber; + } + + @Override + public void onEvent(AttributeChangedMessage message) { + GenericConverter converter = clusterToConverters.get(message.path.clusterId); + if (converter != null) { + converter.onEvent(message); + } else { + logger.debug("onEvent: No converter found for cluster: {}", message.path.clusterId); + } + } + + @Override + public void onEvent(EventTriggeredMessage message) { + GenericConverter converter = clusterToConverters.get(message.path.clusterId); + if (converter != null) { + converter.onEvent(message); + } else { + logger.debug("onEvent: No converter found for cluster: {}", message.path.clusterId); + } + } + + /** + * Handles a openHAB command for a specific channel + * + * @param channelUID The UID of the channel + * @param command The command to handle + */ + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("Handling command for channel: {}", channelUID); + Optional.ofNullable(channelUIDToConverters.get(channelUID)) + .ifPresent(converter -> converter.handleCommand(channelUID, command)); + } + + /** + * Inform all cluster converters to refresh their channel state + */ + public void initState() { + clusterToConverters.forEach((clusterId, converter) -> converter.initState()); + } + + /** + * Returns the endpoint number for this device + * + * @return The endpoint number + */ + public Integer endpointNumber() { + return endpointNumber; + } + + /** + * Create openHAB channels for the device type based on the clusters provided + * + * @param endpointNumber The endpoint number that contains the clusters + * @param clusters The clusters to create channels for + * @param channelGroupUID The channel group UID + * @return A list of channels + */ + public final List createChannels(Integer endpointNumber, Map clusters, + ChannelGroupUID channelGroupUID) { + logger.debug("createChannels {}", endpointNumber); + allClusters = clusters; + List channels = new ArrayList<>(); + channelUIDToConverters.clear(); + channelUIDToStateDescription.clear(); + clusterToConverters.clear(); + String label = ""; + // each cluster will create its own channels and add to this device's total channels + clusters.forEach((clusterName, cluster) -> { + logger.debug("Creating channels for cluster: {}", clusterName); + GenericConverter converter = createConverter(cluster, clusters, label); + if (converter != null) { + logger.debug("Converter found for cluster: {}", clusterName); + clusterToConverters.put(cluster.id, converter); + Map converterChannels = converter.createChannels(channelGroupUID); + for (Channel channel : converterChannels.keySet()) { + channelUIDToConverters.put(channel.getUID(), converter); + channelUIDToStateDescription.put(channel.getUID(), converterChannels.get(channel)); + boolean hasMatchingUID = channels.stream().anyMatch(c -> channel.getUID().equals(c.getUID())); + if (!hasMatchingUID) { + channels.add(channel); + } else { + logger.debug("{} channel already exists: {}", clusterName, channel.getUID()); + } + } + } + }); + return channels; + } + + /** + * Returns an unmodifiable map of channel UID to state description for this device type. + */ + public Map getStateDescriptions() { + return Collections.unmodifiableMap(new HashMap<>(channelUIDToStateDescription)); + } + + /** + * Calls the pollCluster method on all cluster converters + */ + public void pollClusters() { + clusterToConverters.forEach((clusterId, converter) -> { + converter.pollCluster(); + }); + } + + /** + * Returns an unmodifiable map of all clusters associated with this device type. + * The map keys are cluster names + */ + public Map getAllClusters() { + return Collections.unmodifiableMap(allClusters); + } + + /** + * Returns an unmodifiable map of cluster converters associated with this device type. + * The map keys are cluster IDs + */ + public Map> getClusterConverters() { + return Collections.unmodifiableMap(clusterToConverters); + } + + // This method is designed to be overridden in subclasses + protected @Nullable GenericConverter createConverter(BaseCluster cluster, + Map allClusters, String labelPrefix) { + try { + return ConverterRegistry.createConverter(cluster, handler, endpointNumber, labelPrefix); + } catch (ConverterRegistry.NoConverterFoundException e) { + logger.debug("No converter found for cluster: {}", cluster.id); + return null; + } catch (ConverterRegistry.ConverterCreationException e) { + logger.debug("Error creating converter for cluster: {}", cluster.id, e); + return null; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/DeviceTypeRegistry.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/DeviceTypeRegistry.java new file mode 100644 index 00000000000..9eb23448ac9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/DeviceTypeRegistry.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.types; + +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DeviceTypes; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; + +/** + * The {@link DeviceTypeRegistry} is a registry of device types that are supported by the Matter binding. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class DeviceTypeRegistry { + + private static final Map> DEVICE_TYPES = new HashMap<>(); + + static { + List.of(DeviceTypes.ON_OFF_LIGHT, DeviceTypes.ON_OFF_LIGHT_SWITCH, DeviceTypes.ON_OFF_PLUG_IN_UNIT, + DeviceTypes.DIMMABLE_LIGHT, DeviceTypes.DIMMABLE_PLUG_IN_UNIT, DeviceTypes.DIMMER_SWITCH, + DeviceTypes.COLOR_DIMMER_SWITCH, DeviceTypes.EXTENDED_COLOR_LIGHT, DeviceTypes.COLOR_TEMPERATURE_LIGHT) + .forEach(type -> DeviceTypeRegistry.registerDeviceType(type, LightingType.class)); + } + + /** + * Register a device type with the device type id. + * + * @param deviceTypeId The device type id + * @param deviceType The device type class + */ + public static void registerDeviceType(Integer deviceTypeId, Class deviceType) { + DEVICE_TYPES.put(deviceTypeId, deviceType); + } + + /** + * Create a device type based on the device type id. If the device type is not found, a generic type is returned. + * + * @param deviceTypeId The device type id + * @param handler The handler + * @param endpointNumber The endpoint number + * @return The device type + */ + public static DeviceType createDeviceType(Integer deviceTypeId, MatterBaseThingHandler handler, + Integer endpointNumber) { + Class clazz = DEVICE_TYPES.get(deviceTypeId); + if (clazz != null) { + try { + Class[] constructorParameterTypes = new Class[] { Integer.class, MatterBaseThingHandler.class, + Integer.class }; + Constructor constructor = clazz.getConstructor(constructorParameterTypes); + return constructor.newInstance(deviceTypeId, handler, endpointNumber); + } catch (Exception e) { + // ignore + } + } + return new GenericType(0, handler, endpointNumber); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/GenericType.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/GenericType.java new file mode 100644 index 00000000000..364844881a5 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/GenericType.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.types; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; + +/** + * The {@link GenericType} is a generic type of device and can be used for most Matter devices + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class GenericType extends DeviceType { + + public GenericType(Integer deviceType, MatterBaseThingHandler handler, Integer endpointNumber) { + super(deviceType, handler, endpointNumber); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/LightingType.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/LightingType.java new file mode 100644 index 00000000000..e46faffe8c1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/controller/devices/types/LightingType.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.types; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DeviceTypes; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.controller.devices.converter.ColorControlConverter; +import org.openhab.binding.matter.internal.controller.devices.converter.GenericConverter; +import org.openhab.binding.matter.internal.controller.devices.converter.LevelControlConverter; +import org.openhab.binding.matter.internal.controller.devices.converter.OnOffConverter; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A DeviceType for lighting devices. + * + * Lighting requires special handling for the OnOff, ColorControl and LevelControl clusters. + * For example, the Matter specification mandates Switches also must have a LevelControl cluster, even though + * they do not support dimming. We will filter those clusters out as well as coordinate commands among required + * clusters. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class LightingType extends DeviceType { + private final Logger logger = LoggerFactory.getLogger(LightingType.class); + + public LightingType(Integer deviceType, MatterBaseThingHandler handler, Integer endpointNumber) { + super(deviceType, handler, endpointNumber); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + logger.debug("Handling command for channel: {}", channelUID); + // For dimmer and color lights handling a OnOff type, we send a onOff cluster command as we don't include an + // OnOff converter + if (command instanceof OnOffType onOffType && !clusterToConverters.containsKey(OnOffCluster.CLUSTER_ID)) { + ClusterCommand onOffCommand = onOffType == OnOffType.ON ? OnOffCluster.on() : OnOffCluster.off(); + handler.sendClusterCommand(endpointNumber, OnOffCluster.CLUSTER_NAME, onOffCommand); + } else { + // will process onOff if onOffConverter is present and all other commands to the appropriate converter + super.handleCommand(channelUID, command); + } + } + + @Override + public void onEvent(AttributeChangedMessage message) { + logger.debug("OnEvent: {} with value {}", message.path.attributeName, message.value); + switch (message.path.attributeName) { + case OnOffCluster.ATTRIBUTE_ON_OFF: + case LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL: + if (clusterToConverters.get(OnOffCluster.CLUSTER_ID) instanceof OnOffConverter onOffCluster) { + onOffCluster.onEvent(message); + } + if (clusterToConverters + .get(LevelControlCluster.CLUSTER_ID) instanceof LevelControlConverter levelControlConverter) { + levelControlConverter.onEvent(message); + } + if (clusterToConverters + .get(ColorControlCluster.CLUSTER_ID) instanceof ColorControlConverter colorControlConverter) { + colorControlConverter.onEvent(message); + } + return; + } + // no matching cluster, bubble up for generic cluster processing + super.onEvent(message); + } + + @Override + public void initState() { + // default to on, and let matter tell otherwise + OnOffType lastOnOff = OnOffType.ON; + LevelControlCluster levelControlCluster = null; + if (allClusters.get(OnOffCluster.CLUSTER_NAME) instanceof OnOffCluster onOffCluster) { + lastOnOff = OnOffType.from(onOffCluster.onOff); + } + + if (allClusters.get(LevelControlCluster.CLUSTER_NAME) instanceof LevelControlCluster lcc) { + levelControlCluster = lcc; + } + + final OnOffType finalLastOnOff = lastOnOff; + final LevelControlCluster finalLevelControlCluster = levelControlCluster; + channelUIDToConverters.forEach((channelUID, converter) -> { + if (converter instanceof LevelControlConverter levelControlConverter) { + levelControlConverter.initState(finalLastOnOff == OnOffType.ON); + } else if (converter instanceof ColorControlConverter colorControlConverter) { + colorControlConverter.initState(finalLastOnOff == OnOffType.ON, finalLevelControlCluster); + } else { + converter.initState(); + } + }); + } + + @Override + protected @Nullable GenericConverter createConverter(BaseCluster cluster, + Map allClusters, String labelPrefix) { + logger.debug("checking converter for cluster: {}", cluster.getClass().getSimpleName()); + // Skip creating certain converters that this DeviceType will coordinate + if ((cluster instanceof OnOffCluster && !isSwitch()) + || (cluster instanceof LevelControlCluster && (isSwitch() || isColor()))) { + return null; + } + + return super.createConverter(cluster, allClusters, labelPrefix); + } + + private boolean isSwitch() { + return deviceType.equals(DeviceTypes.ON_OFF_LIGHT) || deviceType.equals(DeviceTypes.ON_OFF_LIGHT_SWITCH) + || deviceType.equals(DeviceTypes.ON_OFF_PLUG_IN_UNIT); + } + + private boolean isColor() { + return deviceType.equals(DeviceTypes.EXTENDED_COLOR_LIGHT) + || deviceType.equals(DeviceTypes.COLOR_TEMPERATURE_LIGHT); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/discovery/MatterDiscoveryHandler.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/discovery/MatterDiscoveryHandler.java new file mode 100644 index 00000000000..bb3d548478d --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/discovery/MatterDiscoveryHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.discovery; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; + +/** + * The {@link MatterDiscoveryHandler} is the interface for the discovery handler. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public interface MatterDiscoveryHandler { + /** + * Sets a {@link MatterDiscoveryService} to call when device information is received + * + */ + public void setDiscoveryService(MatterDiscoveryService service); + + public CompletableFuture startScan(@Nullable String code); + + public LocaleProvider getLocaleProvider(); + + public TranslationProvider getTranslationProvider(); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/discovery/MatterDiscoveryService.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/discovery/MatterDiscoveryService.java new file mode 100644 index 00000000000..ac4f6435aa1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/discovery/MatterDiscoveryService.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.discovery; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.math.BigInteger; +import java.util.Set; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.Node; +import org.openhab.binding.matter.internal.util.MatterLabelUtils; +import org.openhab.core.config.discovery.AbstractDiscoveryService; +import org.openhab.core.config.discovery.DiscoveryResult; +import org.openhab.core.config.discovery.DiscoveryResultBuilder; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link MatterDiscoveryService} is the service that discovers Matter devices and endpoints. + * + * If a code is provided, it will be used to discover a specific device and commission it to our Fabric + * + * If no code is provided, it will scan for existing devices and endpoints and add them to the inbox for further + * processing + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterDiscoveryService extends AbstractDiscoveryService implements ThingHandlerService { + private final Logger logger = LoggerFactory.getLogger(MatterDiscoveryService.class); + private @Nullable ThingHandler thingHandler; + private @Nullable ScheduledFuture discoveryJob; + private static final int REFRESH_INTERVAL = 60 * 5; + + public MatterDiscoveryService() throws IllegalArgumentException { + // set a 2 min timeout, which should be plenty of time to discover devices, but stopScan will be called when the + // Matter client is done looking for new Nodes/Endpoints + super(Set.of(THING_TYPE_NODE, THING_TYPE_ENDPOINT), REFRESH_INTERVAL); + } + + @Override + public void setThingHandler(ThingHandler handler) { + logger.debug("setThingHandler {}", handler); + if (handler instanceof MatterDiscoveryHandler childDiscoveryHandler) { + childDiscoveryHandler.setDiscoveryService(this); + this.thingHandler = handler; + this.i18nProvider = childDiscoveryHandler.getTranslationProvider(); + this.localeProvider = childDiscoveryHandler.getLocaleProvider(); + } + } + + @Override + public @Nullable ThingHandler getThingHandler() { + return thingHandler; + } + + @Override + public void activate() { + super.activate(null); + } + + @Override + public void deactivate() { + super.deactivate(); + stopBackgroundDiscovery(); + } + + @Override + protected void startBackgroundDiscovery() { + logger.debug("Start background discovery"); + ScheduledFuture discoveryJob = this.discoveryJob; + if (discoveryJob == null || discoveryJob.isCancelled()) { + this.discoveryJob = scheduler.scheduleWithFixedDelay(this::startScan, REFRESH_INTERVAL, REFRESH_INTERVAL, + TimeUnit.SECONDS); + } + } + + @Override + protected void stopBackgroundDiscovery() { + logger.debug("Stop background discovery"); + ScheduledFuture discoveryJob = this.discoveryJob; + if (discoveryJob != null) { + discoveryJob.cancel(true); + this.discoveryJob = null; + } + } + + @Override + public @Nullable String getScanInputLabel() { + return DISCOVERY_MATTER_SCAN_INPUT_LABEL; + } + + @Override + public @Nullable String getScanInputDescription() { + return DISCOVERY_MATTER_SCAN_INPUT_DESCRIPTION; + } + + @Override + protected void startScan() { + startScan(""); + } + + @Override + public void startScan(String input) { + ThingHandler handler = this.thingHandler; + if (handler != null && handler instanceof MatterDiscoveryHandler childDiscoveryHandler) { + childDiscoveryHandler.startScan(input.length() > 0 ? input : null).whenComplete((value, e) -> { + logger.debug("startScan complete"); + stopScan(); + }); + } + } + + public void discoverBridgeEndpoint(ThingUID thingUID, ThingUID bridgeUID, Endpoint root) { + String label = String.format("%s [\"%s\"]", DISCOVERY_MATTER_BRIDGE_ENDPOINT_LABEL, + MatterLabelUtils.labelForBridgeEndpoint(root)).trim(); + discoverThing(thingUID, bridgeUID, root, root.number.toString(), "endpointId", label); + } + + public void discoverNodeDevice(ThingUID thingUID, ThingUID bridgeUID, Node node) { + String label = String.format("%s [\"%s\"]", DISCOVERY_MATTER_NODE_DEVICE_LABEL, + MatterLabelUtils.labelForNode(node.rootEndpoint)).trim(); + discoverThing(thingUID, bridgeUID, node.rootEndpoint, node.id.toString(), "nodeId", label); + } + + public void discoverUnknownNodeDevice(ThingUID thingUID, ThingUID bridgeUID, BigInteger nodeId) { + DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel(DISCOVERY_MATTER_UNKNOWN_NODE_LABEL) + .withProperty("nodeId", nodeId.toString()).withRepresentationProperty("nodeId").withBridge(bridgeUID) + .build(); + thingDiscovered(result); + } + + private void discoverThing(ThingUID thingUID, ThingUID bridgeUID, Endpoint root, String id, + String representationProperty, String label) { + logger.debug("discoverThing: {} {} {} {}", label, thingUID, bridgeUID, id); + DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withLabel(label) + .withProperty(representationProperty, id).withRepresentationProperty(representationProperty) + .withBridge(bridgeUID).build(); + thingDiscovered(result); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/ControllerHandler.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/ControllerHandler.java new file mode 100644 index 00000000000..b728f0619b9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/ControllerHandler.java @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.handler; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.*; + +import java.io.File; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.actions.MatterControllerActions; +import org.openhab.binding.matter.internal.client.MatterClientListener; +import org.openhab.binding.matter.internal.client.MatterWebsocketService; +import org.openhab.binding.matter.internal.client.dto.Node; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.BridgeEventMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeDataMessage; +import org.openhab.binding.matter.internal.client.dto.ws.NodeStateMessage; +import org.openhab.binding.matter.internal.config.ControllerConfiguration; +import org.openhab.binding.matter.internal.controller.MatterControllerClient; +import org.openhab.binding.matter.internal.discovery.MatterDiscoveryHandler; +import org.openhab.binding.matter.internal.discovery.MatterDiscoveryService; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.OpenHAB; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseBridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.types.Command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@link ControllerHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * A Controller is a Matter client that is used to discover and link devices to + * the Matter network, as well as to send commands to devices and receive events + * from them. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ControllerHandler extends BaseBridgeHandler implements MatterClientListener, MatterDiscoveryHandler { + + private final Logger logger = LoggerFactory.getLogger(ControllerHandler.class); + private static final int CONNECTION_TIMEOUT_MS = 180000; // 3 minutes + private final MatterWebsocketService websocketService; + // Set of nodes we are waiting to connect to + private Set outstandingNodeRequests = Collections.synchronizedSet(new HashSet<>()); + // Set of nodes we need to try reconnecting to + private Set disconnectedNodes = Collections.synchronizedSet(new HashSet<>()); + // Nodes that we have linked to a handler + private Map linkedNodes = Collections.synchronizedMap(new HashMap<>()); + private @Nullable MatterDiscoveryService discoveryService; + private @Nullable ScheduledFuture reconnectFuture; + private MatterControllerClient client; + private boolean ready = false; + private final TranslationService translationService; + + public ControllerHandler(Bridge bridge, MatterWebsocketService websocketService, + TranslationService translationService) { + super(bridge); + client = new MatterControllerClient(); + client.addListener(this); + this.websocketService = websocketService; + this.translationService = translationService; + } + + @Override + public Collection> getServices() { + return Set.of(MatterDiscoveryService.class, MatterControllerActions.class); + } + + @Override + public void initialize() { + logger.debug("initialize"); + connect(); + } + + @Override + public void dispose() { + logger.debug("dispose"); + ready = false; + client.removeListener(this); + cancelReconnect(); + outstandingNodeRequests.clear(); + disconnectedNodes.clear(); + linkedNodes.clear(); + client.disconnect(); + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + } + + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + super.childHandlerInitialized(childHandler, childThing); + logger.debug("childHandlerInitialized ready {} {}", ready, childHandler); + if (childHandler instanceof NodeHandler handler) { + BigInteger nodeId = handler.getNodeId(); + linkedNodes.put(nodeId, handler); + updateNode(nodeId); + } + } + + @Override + public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { + super.childHandlerDisposed(childHandler, childThing); + logger.debug("childHandlerDisposed {}", childHandler); + if (!ready) { + return; + } + if (childHandler instanceof NodeHandler handler) { + removeNode(handler.getNodeId()); + try { + client.disconnectNode(handler.getNodeId()).get(); + } catch (InterruptedException | ExecutionException e) { + logger.debug("Could not disconnect node {}", handler.getNodeId(), e); + } + + } + } + + @Override + public void setDiscoveryService(@Nullable MatterDiscoveryService service) { + logger.debug("setDiscoveryService"); + this.discoveryService = service; + } + + public @Nullable MatterDiscoveryService getDiscoveryService() { + return discoveryService; + } + + @Override + public CompletableFuture startScan(@Nullable String code) { + if (!client.isConnected()) { + logger.debug("not connected"); + return CompletableFuture.completedFuture(null); + } + if (code != null) { + return client.pairNode(code).thenCompose(nodeId -> updateNode(nodeId)); + } else { + // If no code, just sync unknown nodes + return syncUnknownNodes(); + } + } + + @Override + public LocaleProvider getLocaleProvider() { + return translationService.getLocaleProvider(); + } + + @Override + public TranslationProvider getTranslationProvider() { + return translationService.getTranslationProvider(); + } + + @Override + public void onEvent(NodeStateMessage message) { + logger.debug("Node onEvent: node {} is {}", message.nodeId, message.state); + switch (message.state) { + case CONNECTED: + updateEndpointStatuses(message.nodeId, ThingStatus.UNKNOWN, ThingStatusDetail.NONE, + translationService.getTranslation(THING_STATUS_DETAIL_CONTROLLER_WAITING_FOR_DATA)); + client.requestAllNodeData(message.nodeId); + break; + case STRUCTURECHANGED: + updateNode(message.nodeId); + break; + case DECOMMISSIONED: + updateEndpointStatuses(message.nodeId, ThingStatus.OFFLINE, ThingStatusDetail.GONE, + "Node " + message.state); + removeNode(message.nodeId); + break; + case DISCONNECTED: + if (linkedNodes.containsKey(message.nodeId)) { + disconnectedNodes.add(message.nodeId); + } + // fall through + case RECONNECTING: + case WAITINGFORDEVICEDISCOVERY: + updateEndpointStatuses(message.nodeId, ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + "Node " + message.state); + break; + default: + } + } + + @Override + public void onEvent(AttributeChangedMessage message) { + NodeHandler handler = linkedNodes.get(message.path.nodeId); + if (handler == null) { + logger.debug("No handler found for node {}", message.path.nodeId); + return; + } + handler.onEvent(message); + } + + @Override + public void onEvent(EventTriggeredMessage message) { + NodeHandler handler = linkedNodes.get(message.path.nodeId); + if (handler == null) { + logger.debug("No handler found for node {}", message.path.nodeId); + return; + } + handler.onEvent(message); + } + + @Override + public void onEvent(NodeDataMessage message) { + logger.debug("NodeDataMessage onEvent: node {} is {}", message.node.id, message.node); + updateNode(message.node); + } + + @Override + public void onEvent(BridgeEventMessage message) { + } + + @Override + public void onConnect() { + logger.debug("Websocket connected"); + } + + @Override + public void onDisconnect(String reason) { + logger.debug("websocket disconnected"); + setOffline(reason); + } + + @Override + public void onReady() { + logger.debug("websocket ready"); + ready = true; + updateStatus(ThingStatus.ONLINE); + cancelReconnect(); + linkedNodes.keySet().forEach(nodeId -> updateNode(nodeId)); + } + + public String getTranslation(String key) { + return translationService.getTranslation(key); + } + + public MatterControllerClient getClient() { + return client; + } + + public Map getLinkedNodes() { + return linkedNodes; + } + + protected void removeNode(BigInteger nodeId) { + logger.debug("removing node {}", nodeId); + disconnectedNodes.remove(nodeId); + outstandingNodeRequests.remove(nodeId); + linkedNodes.remove(nodeId); + } + + /** + * Update the node with the given id + * If the node is not currently linked, it will be added to the discovery service + * + * @param id + * @return + */ + protected CompletableFuture updateNode(BigInteger id) { + logger.debug("updateNode BEGIN {}", id); + + // If we are already waiting to get this node, return a completed future + synchronized (this) { + // If we are already waiting to get this node, return a completed future + if (!ready || outstandingNodeRequests.contains(id)) { + return CompletableFuture.completedFuture(null); + } + outstandingNodeRequests.add(id); + } + + return client.initializeNode(id, CONNECTION_TIMEOUT_MS).thenAccept((Void) -> { + disconnectedNodes.remove(id); + client.requestAllNodeData(id); + logger.debug("updateNode END {}", id); + }).exceptionally(e -> { + logger.debug("Could not update node {}", id, e); + disconnectedNodes.add(id); + String message = e.getMessage(); + updateEndpointStatuses(id, ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + message != null ? message : ""); + throw new CompletionException(e); + }).whenComplete((node, e) -> outstandingNodeRequests.remove(id)); + } + + protected CompletableFuture updateEndpoint(BigInteger nodeId, Integer endpointId) { + return client.requestEndpointData(nodeId, endpointId); + } + + /** + * Update the endpoints (devices) for a node + * + * @param node + */ + private synchronized void updateNode(Node node) { + NodeHandler handler = linkedNodes.get(node.id); + disconnectedNodes.remove(node.id); + if (handler != null) { + handler.updateNode(node); + } else { + discoverChildNode(node); + } + } + + private void connect() { + logger.debug("connect"); + if (client.isConnected()) { + logger.debug("Client already connected"); + return; + } + String folderName = OpenHAB.getUserDataFolder() + File.separator + "matter"; + File folder = new File(folderName); + if (!folder.exists()) { + folder.mkdirs(); + } + String storagePath = folder.getAbsolutePath(); + String controllerName = "controller-" + getThing().getUID().getId(); + + logger.debug("matter config: {}", storagePath); + final ControllerConfiguration config = getConfigAs(ControllerConfiguration.class); + client.connect(websocketService, new BigInteger(config.nodeId), controllerName, storagePath); + } + + /** + * Discover all unknown nodes from the controller + * + * @return + */ + private CompletableFuture syncUnknownNodes() { + logger.debug("syncUnknownNodes starting"); + if (!ready) { + return CompletableFuture.completedFuture(null); + } + return client.getCommissionedNodeIds().thenCompose(nodeIds -> { + List> futures = new ArrayList<>(); + for (BigInteger id : nodeIds) { + // ignore nodes that are already linked and have no bridged endpoints + NodeHandler handler = linkedNodes.get(id); + if (handler != null) { + if (!handler.hasBridgedEndpoints()) { + continue; + } + } + // updateNode will add the node to the discovery service, if it fails, we will add it to the discovery + // service manually (orphaned node) + futures.add(updateNode(id).exceptionally(e -> { + logger.debug("Failed to sync node {}: {}", id, e.getMessage()); + MatterDiscoveryService discoveryService = this.discoveryService; + if (discoveryService != null) { + ThingUID bridgeUID = getThing().getUID(); + ThingUID thingUID = new ThingUID(THING_TYPE_NODE, bridgeUID, id.toString()); + discoveryService.discoverUnknownNodeDevice(thingUID, bridgeUID, id); + } + return Void.TYPE.cast(null); // ugly but it works + })); + } + // Return a Future that completes when all updateNode futures are complete + return futures.isEmpty() ? CompletableFuture.completedFuture(null) + : CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + }).exceptionally(e -> { + logger.debug("Error getting commissioned nodes from controller", e); + // Convert the exception into a failed future instead of swallowing it + throw new CompletionException(e); + }).whenComplete((v, e) -> logger.debug("refresh done {}", e != null ? " with error: " + e.getMessage() : "")); + } + + private synchronized void reconnect() { + logger.debug("reconnect!"); + cancelReconnect(); + this.reconnectFuture = scheduler.schedule(this::connect, 30, TimeUnit.SECONDS); + } + + private synchronized void cancelReconnect() { + ScheduledFuture reconnectFuture = this.reconnectFuture; + if (reconnectFuture != null) { + reconnectFuture.cancel(true); + } + this.reconnectFuture = null; + } + + private void updateEndpointStatuses(BigInteger nodeId, ThingStatus status, ThingStatusDetail detail, + String details) { + for (Thing thing : getThing().getThings()) { + ThingHandler handler = thing.getHandler(); + if (handler instanceof NodeHandler endpointHandler) { + if (nodeId.equals(endpointHandler.getNodeId())) { + endpointHandler.setEndpointStatus(status, detail, details); + } + } + } + } + + private void setOffline(@Nullable String message) { + logger.debug("setOffline {}", message); + client.disconnect(); + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message); + reconnect(); + } + + private void discoverChildNode(Node node) { + logger.debug("discoverChildNode {}", node.id); + + MatterDiscoveryService discoveryService = this.discoveryService; + if (discoveryService != null) { + ThingUID bridgeUID = getThing().getUID(); + ThingUID thingUID = new ThingUID(THING_TYPE_NODE, bridgeUID, node.id.toString()); + discoveryService.discoverNodeDevice(thingUID, bridgeUID, node); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/EndpointHandler.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/EndpointHandler.java new file mode 100644 index 00000000000..29cc318dba4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/EndpointHandler.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.handler; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.THING_STATUS_DETAIL_ENDPOINT_THING_NOT_REACHABLE; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterChannelTypeProvider; +import org.openhab.binding.matter.internal.MatterConfigDescriptionProvider; +import org.openhab.binding.matter.internal.MatterStateDescriptionOptionProvider; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BridgedDeviceBasicInformationCluster; +import org.openhab.binding.matter.internal.config.EndpointConfiguration; +import org.openhab.binding.matter.internal.util.MatterUIDUtils; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.BridgeHandler; + +/** + * The {@link EndpointHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * This class is used for handling endpoints that are "bridged" devices, although it could be used to handle any + * endpoint(s) if needed, otherwise Endpoints are generally managed as channel groups on a Node Thing + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class EndpointHandler extends MatterBaseThingHandler { + private Integer endpointId = 0; + private Integer pollInterval = 0; + + public EndpointHandler(Thing thing, BaseThingHandlerFactory thingHandlerFactory, + MatterStateDescriptionOptionProvider stateDescriptionProvider, + MatterChannelTypeProvider channelGroupTypeProvider, + MatterConfigDescriptionProvider configDescriptionProvider, TranslationService translationService) { + super(thing, thingHandlerFactory, stateDescriptionProvider, channelGroupTypeProvider, configDescriptionProvider, + translationService); + } + + @Override + public void initialize() { + EndpointConfiguration config = getConfigAs(EndpointConfiguration.class); + endpointId = config.endpointId; + pollInterval = config.pollInterval; + logger.debug("initialize bridge endpoint {}", config.endpointId); + super.initialize(); + } + + @Override + public BigInteger getNodeId() { + NodeHandler nodeHandler = nodeHandler(); + if (nodeHandler != null) { + return nodeHandler.getNodeId(); + } + throw new IllegalStateException("Could not access parent bridge!"); + } + + @Override + public ThingTypeUID getDynamicThingTypeUID() { + return MatterUIDUtils.endpointThingTypeUID(getNodeId(), endpointId); + } + + @Override + public boolean isBridgeType() { + return false; + } + + @Override + public Integer getPollInterval() { + return pollInterval; + } + + @Override + protected void updateBaseEndpoint(Endpoint endpoint) { + boolean reachable = true; + BaseCluster basicInfoObject = endpoint.clusters.get(BridgedDeviceBasicInformationCluster.CLUSTER_NAME); + if (basicInfoObject != null) { + BridgedDeviceBasicInformationCluster basicInfo = (BridgedDeviceBasicInformationCluster) basicInfoObject; + if (basicInfo.reachable != null) { + reachable = basicInfo.reachable; + } + } + if (reachable) { + if (getThing().getStatus() != ThingStatus.ONLINE) { + logger.debug("Setting Online {}", endpointId); + updateStatus(ThingStatus.ONLINE); + } + } else { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, + getTranslation(THING_STATUS_DETAIL_ENDPOINT_THING_NOT_REACHABLE)); + } + updateRootProperties(endpoint); + super.updateBaseEndpoint(endpoint); + } + + public Integer getEndpointId() { + return endpointId; + } + + private @Nullable NodeHandler nodeHandler() { + Bridge bridge = getBridge(); + if (bridge != null) { + BridgeHandler handler = bridge.getHandler(); + if (handler instanceof NodeHandler nodeHandler) { + return nodeHandler; + } + } + return null; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/MatterBaseThingHandler.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/MatterBaseThingHandler.java new file mode 100644 index 00000000000..3ae0c5f6719 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/MatterBaseThingHandler.java @@ -0,0 +1,595 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.handler; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.THING_STATUS_DETAIL_CONTROLLER_WAITING_FOR_DATA; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterBindingConstants; +import org.openhab.binding.matter.internal.MatterChannelTypeProvider; +import org.openhab.binding.matter.internal.MatterConfigDescriptionProvider; +import org.openhab.binding.matter.internal.MatterStateDescriptionOptionProvider; +import org.openhab.binding.matter.internal.client.AttributeListener; +import org.openhab.binding.matter.internal.client.EventTriggeredListener; +import org.openhab.binding.matter.internal.client.MatterWebsocketClient; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.cluster.ClusterCommand; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BasicInformationCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BridgedDeviceBasicInformationCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.GeneralDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.GeneralDiagnosticsCluster.NetworkInterface; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.controller.MatterControllerClient; +import org.openhab.binding.matter.internal.controller.devices.converter.GenericConverter; +import org.openhab.binding.matter.internal.controller.devices.types.DeviceType; +import org.openhab.binding.matter.internal.controller.devices.types.DeviceTypeRegistry; +import org.openhab.binding.matter.internal.util.MatterLabelUtils; +import org.openhab.binding.matter.internal.util.MatterUIDUtils; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.config.core.ConfigDescription; +import org.openhab.core.config.core.Configuration; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandler; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.binding.builder.ThingBuilder; +import org.openhab.core.thing.type.ChannelGroupDefinition; +import org.openhab.core.thing.type.ChannelGroupType; +import org.openhab.core.thing.type.ChannelGroupTypeBuilder; +import org.openhab.core.thing.type.ChannelGroupTypeUID; +import org.openhab.core.thing.type.ThingTypeBuilder; +import org.openhab.core.types.Command; +import org.openhab.core.types.State; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; + +/** + * The {@link MatterBaseThingHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * This class is the base class for all Matter device types. It is responsible for creating the channels and channel + * groups for each Node or Bridge Endpoint. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public abstract class MatterBaseThingHandler extends BaseThingHandler + implements AttributeListener, EventTriggeredListener { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + protected final BaseThingHandlerFactory thingHandlerFactory; + protected final MatterStateDescriptionOptionProvider stateDescriptionProvider; + protected final MatterChannelTypeProvider channelTypeProvider; + protected final MatterConfigDescriptionProvider configDescriptionProvider; + protected final TranslationService translationService; + protected Map devices = new HashMap<>(); + protected @Nullable MatterControllerClient cachedClient; + private @Nullable ScheduledFuture pollingTask; + + public MatterBaseThingHandler(Thing thing, BaseThingHandlerFactory thingHandlerFactory, + MatterStateDescriptionOptionProvider stateDescriptionProvider, + MatterChannelTypeProvider channelTypeProvider, MatterConfigDescriptionProvider configDescriptionProvider, + TranslationService translationService) { + super(thing); + this.thingHandlerFactory = thingHandlerFactory; + this.stateDescriptionProvider = stateDescriptionProvider; + this.channelTypeProvider = channelTypeProvider; + this.configDescriptionProvider = configDescriptionProvider; + this.translationService = translationService; + } + + public abstract BigInteger getNodeId(); + + public abstract Integer getPollInterval(); + + protected abstract ThingTypeUID getDynamicThingTypeUID(); + + protected abstract boolean isBridgeType(); + + /** + * When processing endpoints, give implementers the ability to ignore certain endpoints + * + * @param endpoint the endpoint to check + */ + protected boolean shouldAddEndpoint(Endpoint endpoint) { + return true; + } + + @Override + public void handleCommand(ChannelUID channelUID, Command command) { + MatterWebsocketClient client = getClient(); + if (client == null) { + logger.debug("Matter client not present, ignoring command"); + return; + } + + String endpointIdString = channelUID.getGroupId(); + if (endpointIdString != null) { + Integer endpointId = Integer.valueOf(endpointIdString); + DeviceType deviceType = this.devices.get(endpointId); + if (deviceType != null) { + deviceType.handleCommand(channelUID, command); + } + } + } + + @Override + public void initialize() { + if (getThing().getStatus() != ThingStatus.ONLINE) { + updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, + translationService.getTranslation(THING_STATUS_DETAIL_CONTROLLER_WAITING_FOR_DATA)); + } + } + + @Override + public void dispose() { + channelTypeProvider.removeChannelGroupTypesForPrefix(getThing().getThingTypeUID().getId()); + stopPolling(); + super.dispose(); + } + + @Override + public void handleRemoval() { + channelTypeProvider.removeThingType(getThing().getThingTypeUID()); + channelTypeProvider.removeChannelGroupTypesForPrefix(thing.getThingTypeUID().getId()); + super.handleRemoval(); + } + + @Override + protected ThingBuilder editThing() { + return ThingBuilder.create(getDynamicThingTypeUID(), getThing().getUID()).withBridge(getThing().getBridgeUID()) + .withChannels(getThing().getChannels()).withConfiguration(getThing().getConfiguration()) + .withLabel(getThing().getLabel()).withLocation(getThing().getLocation()) + .withProperties(getThing().getProperties()); + } + + @Override + protected void updateStatus(ThingStatus status, ThingStatusDetail statusDetail, @Nullable String description) { + super.updateStatus(status, statusDetail, description); + if (status != ThingStatus.ONLINE) { + stopPolling(); + } else { + startPolling(); + } + } + + @Override + public void onEvent(AttributeChangedMessage message) { + logger.debug("AttributeChangedMessage for endpoint {}", message.path.endpointId); + DeviceType deviceType = devices.get(message.path.endpointId); + if (deviceType != null) { + deviceType.onEvent(message); + } + } + + @Override + public void onEvent(EventTriggeredMessage message) { + DeviceType deviceType = devices.get(message.path.endpointId); + if (deviceType != null) { + deviceType.onEvent(message); + } + } + + /** + * Update the state of a channel. + * + * @param endpointNumber The endpoint number. + * @param channelId The channel ID. + * @param state The state to update. + */ + public void updateState(Integer endpointNumber, String channelId, State state) { + ChannelGroupUID channelGroupUID = new ChannelGroupUID(getThing().getUID(), endpointNumber.toString()); + ChannelUID channelUID = new ChannelUID(channelGroupUID, channelId); + super.updateState(channelUID, state); + } + + /** + * Trigger a channel event. + * + * @param endpointNumber The endpoint number. + * @param channelId The channel ID. + * @param event The event to trigger. + */ + public void triggerChannel(Integer endpointNumber, String channelId, String event) { + ChannelGroupUID channelGroupUID = new ChannelGroupUID(getThing().getUID(), endpointNumber.toString()); + ChannelUID channelUID = new ChannelUID(channelGroupUID, channelId); + super.triggerChannel(channelUID, event); + } + + /** + * Set the thing status of the endpoint. + * + * @param status The status to set. + * @param detail The detail of the status. + * @param description The description of the status. + */ + public void setEndpointStatus(ThingStatus status, ThingStatusDetail detail, String description) { + logger.debug("setEndpointStatus {} {} {} {}", status, detail, description, getNodeId()); + updateStatus(status, detail, description); + } + + /** + * Send a cluster command to the MatterControllerClient. + * + * @param endpointId The endpoint ID. + * @param clusterName The cluster name. + * @param command The command to send. + * @return CompletableFuture + */ + public CompletableFuture sendClusterCommand(Integer endpointId, String clusterName, + ClusterCommand command) { + MatterControllerClient client = getClient(); + if (client != null) { + logger.debug("sendClusterCommand {} {} {}", getNodeId(), endpointId, clusterName); + return client.clusterCommand(getNodeId(), endpointId, clusterName, command); + } + throw new IllegalStateException("Client is null"); + } + + /** + * Write an attribute to the MatterControllerClient. + * + * @param endpointId The endpoint ID. + * @param clusterName The cluster name. + * @param attributeName The attribute name. + * @param value The value to write. + */ + public void writeAttribute(Integer endpointId, String clusterName, String attributeName, String value) { + MatterControllerClient ws = getClient(); + if (ws != null) { + ws.clusterWriteAttribute(getNodeId(), endpointId, clusterName, attributeName, value); + } else { + throw new IllegalStateException("Client is null"); + } + } + + /** + * Read an attribute from the MatterControllerClient. + * + * @param endpointId The endpoint ID. + * @param clusterName The cluster name. + * @param attributeName The attribute name. + * @return CompletableFuture + */ + public CompletableFuture readAttribute(Integer endpointId, String clusterName, String attributeName) { + MatterControllerClient ws = getClient(); + if (ws != null) { + return ws.clusterReadAttribute(getNodeId(), endpointId, clusterName, attributeName); + } + throw new IllegalStateException("Client is null"); + } + + /** + * Read a cluster from the MatterControllerClient. + * + * @param The type of the cluster to read. + * @param type The class of the cluster to read. + * @param endpointId The endpoint ID. + * @param clusterId The cluster ID. + * @return CompletableFuture + * @throws JsonParseException when completing the future if the cluster cannot be deserialized + */ + public CompletableFuture readCluster(Class type, Integer endpointId, + Integer clusterId) { + MatterControllerClient ws = getClient(); + if (ws != null) { + return ws.readCluster(type, getNodeId(), endpointId, clusterId); + } + throw new IllegalStateException("Client is null"); + } + + /** + * Get the MatterControllerClient instance. + * + * @return The MatterControllerClient instance, or null if not available. + */ + public @Nullable MatterControllerClient getClient() { + if (cachedClient == null) { + ControllerHandler c = controllerHandler(); + if (c != null) { + cachedClient = c.getClient(); + } + } + return cachedClient; + } + + /** + * Update the property of the thing for a given cluster and attribute name. Null values will remove the property. + * + * @param clusterName The name of the cluster. + * @param attributeName The name of the attribute. + * @param value The value of the attribute. + */ + public void updateClusterAttributeProperty(String clusterName, String attributeName, @Nullable Object value) { + getThing().setProperty(clusterName + "-" + attributeName, value != null ? value.toString() : null); + } + + public synchronized void updateConfiguration(Map entries) { + Configuration config = editConfiguration(); + for (Map.Entry entry : entries.entrySet()) { + config.put(entry.getKey(), entry.getValue()); + } + updateConfiguration(config); + } + + /** + * Registers a service with the thing handler factory. + * + * @param klass The class of the service to register. + */ + public void registerService(Class klass) { + thingHandlerFactory.registerService(this, klass); + } + + /** + * Finds the first matching converter of the specified type across all managed devices (endpoints). + * + * @param The type of the converter to find. + * @param converterType The class of the converter to find (e.g., OnOffConverter.class). + * @return The first matching converter of the specified type, or null if none is found. + */ + public > @Nullable T findConverterByType(Class converterType) { + for (DeviceType deviceType : devices.values()) { + for (GenericConverter converter : deviceType.getClusterConverters().values()) { + if (converterType.isInstance(converter)) { + return converterType.cast(converter); + } + } + } + return null; + } + + public void addConfigDescription(ConfigDescription configDescription) { + logger.debug("addConfigDescription {}", configDescription.getUID()); + configDescriptionProvider.addConfigDescription(configDescription); + } + + public @Nullable ConfigDescription getConfigDescription() { + return configDescriptionProvider.getConfigDescription(getConfigDescriptionURI(), Locale.getDefault()); + } + + public URI getConfigDescriptionURI() { + logger.debug("getConfigDescriptionURI {}:{}", MatterBindingConstants.CONFIG_DESCRIPTION_URI_THING_PREFIX, + getThing().getUID().getAsString()); + return URI.create( + MatterBindingConstants.CONFIG_DESCRIPTION_URI_THING_PREFIX + ":" + getThing().getUID().getAsString()); + } + + public final String getTranslation(String key) { + return translationService.getTranslation(key); + } + + protected @Nullable ControllerHandler controllerHandler() { + Bridge bridge = getBridge(); + while (bridge != null) { + BridgeHandler handler = bridge.getHandler(); + if (handler instanceof ControllerHandler controllerHandler) { + return controllerHandler; + } else if (handler instanceof MatterBaseThingHandler matterBaseThingHandler) { + bridge = matterBaseThingHandler.getBridge(); + } + + } + return null; + } + + protected void updateRootProperties(Endpoint root) { + BaseCluster cluster = root.clusters.get(BasicInformationCluster.CLUSTER_NAME); + if (cluster != null && cluster instanceof BasicInformationCluster basicCluster) { + updateClusterAttributeProperty(BasicInformationCluster.CLUSTER_NAME, Thing.PROPERTY_SERIAL_NUMBER, + basicCluster.serialNumber); + updateClusterAttributeProperty(BasicInformationCluster.CLUSTER_NAME, Thing.PROPERTY_FIRMWARE_VERSION, + basicCluster.softwareVersionString); + updateClusterAttributeProperty(BasicInformationCluster.CLUSTER_NAME, Thing.PROPERTY_VENDOR, + basicCluster.vendorName); + updateClusterAttributeProperty(BasicInformationCluster.CLUSTER_NAME, Thing.PROPERTY_MODEL_ID, + basicCluster.productName); + updateClusterAttributeProperty(BasicInformationCluster.CLUSTER_NAME, Thing.PROPERTY_HARDWARE_VERSION, + basicCluster.hardwareVersionString); + } else { + cluster = root.clusters.get(BridgedDeviceBasicInformationCluster.CLUSTER_NAME); + if (cluster != null && cluster instanceof BridgedDeviceBasicInformationCluster basicCluster) { + updateClusterAttributeProperty(BridgedDeviceBasicInformationCluster.CLUSTER_NAME, + Thing.PROPERTY_SERIAL_NUMBER, basicCluster.serialNumber); + updateClusterAttributeProperty(BridgedDeviceBasicInformationCluster.CLUSTER_NAME, + Thing.PROPERTY_FIRMWARE_VERSION, basicCluster.softwareVersionString); + updateClusterAttributeProperty(BridgedDeviceBasicInformationCluster.CLUSTER_NAME, Thing.PROPERTY_VENDOR, + basicCluster.vendorName); + updateClusterAttributeProperty(BridgedDeviceBasicInformationCluster.CLUSTER_NAME, + Thing.PROPERTY_MODEL_ID, basicCluster.productName); + updateClusterAttributeProperty(BridgedDeviceBasicInformationCluster.CLUSTER_NAME, + Thing.PROPERTY_HARDWARE_VERSION, basicCluster.hardwareVersionString); + } + } + cluster = root.clusters.get(GeneralDiagnosticsCluster.CLUSTER_NAME); + if (cluster != null && cluster instanceof GeneralDiagnosticsCluster generalCluster) { + List allIpv6Addresses = new ArrayList<>(); + List allIpv4Addresses = new ArrayList<>(); + for (NetworkInterface ni : generalCluster.networkInterfaces) { + updateClusterAttributeProperty(GeneralDiagnosticsCluster.CLUSTER_NAME, Thing.PROPERTY_MAC_ADDRESS, + MatterLabelUtils.formatMacAddress(ni.hardwareAddress)); + if (!ni.iPv6Addresses.isEmpty()) { + allIpv6Addresses.addAll(ni.iPv6Addresses.stream().map(MatterLabelUtils::formatIPv6Address) + .collect(Collectors.toList())); + } + if (!ni.iPv4Addresses.isEmpty()) { + allIpv4Addresses.addAll(ni.iPv4Addresses.stream().map(MatterLabelUtils::formatIPv4Address) + .collect(Collectors.toList())); + } + } + if (!allIpv6Addresses.isEmpty()) { + updateClusterAttributeProperty(GeneralDiagnosticsCluster.CLUSTER_NAME, + GeneralDiagnosticsCluster.ATTRIBUTE_NETWORK_INTERFACES + "-ipv6", + String.join(",", allIpv6Addresses)); + } + if (!allIpv4Addresses.isEmpty()) { + updateClusterAttributeProperty(GeneralDiagnosticsCluster.CLUSTER_NAME, + GeneralDiagnosticsCluster.ATTRIBUTE_NETWORK_INTERFACES + "-ipv4", + String.join(",", allIpv4Addresses)); + } + // todo remove this after cleanup + getThing().setProperty("ipv6Address", null); + getThing().setProperty("ipv4Address", null); + } + } + + /** + * Main Processing of Matter endpoints. This will create a Channel Group per endpoint + * + * @param endpoint + */ + protected synchronized void updateBaseEndpoint(Endpoint endpoint) { + List groupTypes = new ArrayList<>(); + List groupDefs = new ArrayList<>(); + List channels = new ArrayList<>(); + + updateEndpointInternal(endpoint, groupTypes, groupDefs, channels); + + // update our thing using a custom thing type so we can add channel groups dynamically. + // editThing() is overridden to use the type specific thing type here. + // See https://github.com/openhab/openhab-addons/pull/16600 for more information on this technique + ThingTypeUID baseThingTypeUID = MatterUIDUtils.baseTypeForThingType(getDynamicThingTypeUID()); + ThingTypeUID dynamicThingTypeUID = getDynamicThingTypeUID(); + if (baseThingTypeUID == null) { + throw new IllegalStateException("Could not resolve base thing type for " + getThing().getThingTypeUID()); + } + + // create our dynamic thing from defaults from the xml defined thing, along with the dynamic type of the bridge + // thing + ThingTypeBuilder thingTypeBuilder = channelTypeProvider.derive(dynamicThingTypeUID, baseThingTypeUID, Optional + .ofNullable(getBridge()).map(bridge -> List.of(bridge.getThingTypeUID().toString())).orElse(null)); + + thingTypeBuilder.withChannelGroupDefinitions(groupDefs); + channelTypeProvider.putThingType(isBridgeType() ? thingTypeBuilder.buildBridge() : thingTypeBuilder.build()); + channelTypeProvider.updateChannelGroupTypesForPrefix(dynamicThingTypeUID.getId(), groupTypes); + + ThingBuilder thingBuilder = editThing().withChannels(); + thingBuilder.withChannels(channels); + updateThing(thingBuilder.build()); + + // have all clusters send their initial states + devices.values().forEach(deviceType -> deviceType.initState()); + } + + /** + * Iterate recursively through endpoints, adding channel groups and channels. + * + * @param endpoint + */ + private void updateEndpointInternal(Endpoint endpoint, List groupTypes, + List groupDefs, List channels) { + logger.debug("updateEndpointInternal {}", endpoint.number); + + // give implementers the ability to act on and prevent endpoints from being added (mainly for bridge endpoints) + if (!shouldAddEndpoint(endpoint)) { + return; + } + + Map clusters = endpoint.clusters; + Integer deviceTypeID = MatterLabelUtils.primaryDeviceTypeForEndpoint(endpoint); + String deviceLabel = MatterLabelUtils.labelForEndpoint(endpoint); + + // Do we already have a device created yet? + DeviceType deviceType = devices.get(endpoint.number); + if (deviceType == null) { + deviceType = DeviceTypeRegistry.createDeviceType(deviceTypeID, this, endpoint.number); + devices.put(endpoint.number, deviceType); + logger.debug("updateEndpointInternal added device type {} {}", deviceTypeID, endpoint.number); + } + + // create custom group for endpoint + ChannelGroupTypeUID channelGroupTypeUID = new ChannelGroupTypeUID(MatterBindingConstants.BINDING_ID, + getNodeId() + "-" + endpoint.number); + ChannelGroupDefinition channelGroupDefinition = new ChannelGroupDefinition(endpoint.number.toString(), + channelGroupTypeUID); + ChannelGroupType channelGroupType = ChannelGroupTypeBuilder.instance(channelGroupTypeUID, deviceLabel) + .withDescription(deviceLabel).build(); + groupDefs.add(channelGroupDefinition); + groupTypes.add(channelGroupType); + + ChannelGroupUID channelGroupUID = new ChannelGroupUID(getThing().getUID(), endpoint.number.toString()); + + // add channels, which will be used by the calling 'updateEndpoint' function + channels.addAll(deviceType.createChannels(endpoint.number, clusters, channelGroupUID)); + + // add the state descriptions of channels to our custom stateDescriptionProvider + deviceType.getStateDescriptions().forEach((channelUID, stateDescription) -> { + if (stateDescription != null) { + Optional.ofNullable(stateDescription.getOptions()) + .ifPresent(options -> stateDescriptionProvider.setStateOptions(channelUID, options)); + + Optional.ofNullable(stateDescription.getPattern()) + .ifPresent(pattern -> stateDescriptionProvider.setStatePattern(channelUID, pattern)); + + BigDecimal min = stateDescription.getMinimum(); + BigDecimal max = stateDescription.getMaximum(); + if (min != null && max != null) { + stateDescriptionProvider.setMinMax(channelUID, min, max, stateDescription.getStep(), + stateDescription.getPattern()); + } + } + }); + // add any children recursively (endpoints can have child endpoints) + endpoint.children.forEach(e -> updateEndpointInternal(e, groupTypes, groupDefs, channels)); + } + + /** + * Start polling the device for updates if needed + */ + private synchronized void startPolling() { + stopPolling(); + Integer pollInterval = getPollInterval(); + if (pollInterval > 0) { + pollingTask = scheduler.scheduleWithFixedDelay(() -> { + if (getThing().getStatus() == ThingStatus.ONLINE) { + devices.values().forEach(deviceType -> deviceType.pollClusters()); + } + }, pollInterval, pollInterval, TimeUnit.SECONDS); + } + } + + public void stopPolling() { + ScheduledFuture pollingTask = this.pollingTask; + if (pollingTask != null) { + pollingTask.cancel(true); + this.pollingTask = null; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/NodeHandler.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/NodeHandler.java new file mode 100644 index 00000000000..8540b9cbbd8 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/handler/NodeHandler.java @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.handler; + +import static org.openhab.binding.matter.internal.MatterBindingConstants.THING_TYPE_ENDPOINT; + +import java.math.BigInteger; +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.matter.internal.MatterChannelTypeProvider; +import org.openhab.binding.matter.internal.MatterConfigDescriptionProvider; +import org.openhab.binding.matter.internal.MatterStateDescriptionOptionProvider; +import org.openhab.binding.matter.internal.actions.MatterNodeActions; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.Node; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BridgedDeviceBasicInformationCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.config.NodeConfiguration; +import org.openhab.binding.matter.internal.discovery.MatterDiscoveryService; +import org.openhab.binding.matter.internal.util.MatterUIDUtils; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.thing.Bridge; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.ThingUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.BridgeHandler; +import org.openhab.core.thing.binding.ThingHandler; +import org.openhab.core.thing.binding.ThingHandlerService; +import org.openhab.core.thing.binding.builder.BridgeBuilder; +import org.openhab.core.thing.binding.builder.ThingBuilder; + +/** + * The {@link NodeHandler} is responsible for handling commands, which are + * sent to one of the channels. + * + * This class is used to handle a Matter Node, an IPV6 based device which is made up of a collection of endpoints. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class NodeHandler extends MatterBaseThingHandler implements BridgeHandler { + protected BigInteger nodeId = BigInteger.valueOf(0); + private Integer pollInterval = 0; + private Map bridgedEndpoints = new ConcurrentHashMap<>(); + + public NodeHandler(Bridge bridge, BaseThingHandlerFactory thingHandlerFactory, + MatterStateDescriptionOptionProvider stateDescriptionProvider, + MatterChannelTypeProvider channelGroupTypeProvider, + MatterConfigDescriptionProvider configDescriptionProvider, TranslationService translationService) { + super(bridge, thingHandlerFactory, stateDescriptionProvider, channelGroupTypeProvider, + configDescriptionProvider, translationService); + } + + @Override + public void initialize() { + NodeConfiguration config = getConfigAs(NodeConfiguration.class); + nodeId = new BigInteger(config.nodeId); + pollInterval = config.pollInterval; + logger.debug("initialize node {}", nodeId); + super.initialize(); + } + + @Override + public BigInteger getNodeId() { + return nodeId; + } + + @Override + public ThingTypeUID getDynamicThingTypeUID() { + return MatterUIDUtils.nodeThingTypeUID(getNodeId()); + } + + @Override + public boolean isBridgeType() { + return true; + } + + @Override + public Integer getPollInterval() { + return pollInterval; + } + + @Override + public Bridge getThing() { + return (Bridge) super.getThing(); + } + + @Override + protected ThingBuilder editThing() { + return BridgeBuilder.create(getDynamicThingTypeUID(), getThing().getUID()).withBridge(getThing().getBridgeUID()) + .withChannels(getThing().getChannels()).withConfiguration(getThing().getConfiguration()) + .withLabel(getThing().getLabel()).withLocation(getThing().getLocation()) + .withProperties(getThing().getProperties()); + } + + @Override + public Collection> getServices() { + return Set.of(MatterNodeActions.class); + } + + @Override + protected boolean shouldAddEndpoint(Endpoint endpoint) { + if (endpoint.clusters.containsKey(BridgedDeviceBasicInformationCluster.CLUSTER_NAME)) { + updateBridgeEndpoint(endpoint); + return false; + } + return true; + } + + @Override + public void handleRemoval() { + ControllerHandler bridge = controllerHandler(); + if (bridge != null) { + bridge.removeNode(nodeId); + } + super.handleRemoval(); + } + + @Override + public void childHandlerInitialized(ThingHandler childHandler, Thing childThing) { + logger.debug("childHandlerInitialized ready {}", childHandler); + if (childHandler instanceof EndpointHandler handler) { + bridgedEndpoints.put(handler.getEndpointId(), handler); + // if the node is already online, update the endpoint, otherwise wait for the node to come online which will + // update it. + if (getThing().getStatus() == ThingStatus.ONLINE) { + ControllerHandler ch = controllerHandler(); + if (ch != null) { + ch.updateEndpoint(getNodeId(), handler.getEndpointId()); + } + } + } + } + + @Override + public void childHandlerDisposed(ThingHandler childHandler, Thing childThing) { + if (childHandler instanceof EndpointHandler handler) { + bridgedEndpoints.entrySet().removeIf(entry -> entry.getValue().equals(handler)); + } + } + + @Override + public void onEvent(AttributeChangedMessage message) { + EndpointHandler endpointHandler = bridgedEndpoints.get(message.path.endpointId); + if (endpointHandler != null) { + endpointHandler.onEvent(message); + } else { + super.onEvent(message); + } + } + + @Override + public void onEvent(EventTriggeredMessage message) { + EndpointHandler endpointHandler = bridgedEndpoints.get(message.path.endpointId); + if (endpointHandler != null) { + endpointHandler.onEvent(message); + } else { + super.onEvent(message); + } + } + + @Override + protected synchronized void updateBaseEndpoint(Endpoint endpoint) { + if (getThing().getStatus() != ThingStatus.ONLINE) { + logger.debug("Setting Online {}", getNodeId()); + updateStatus(ThingStatus.ONLINE); + } + super.updateBaseEndpoint(endpoint); + } + + public void updateNode(Node node) { + updateRootProperties(node.rootEndpoint); + updateBaseEndpoint(node.rootEndpoint); + } + + public boolean hasBridgedEndpoints() { + return !bridgedEndpoints.isEmpty(); + } + + private void updateBridgeEndpoint(Endpoint endpoint) { + EndpointHandler handler = bridgedEndpoints.get(endpoint.number); + if (handler != null) { + updateBridgeEndpointMap(endpoint, handler); + handler.updateBaseEndpoint(endpoint); + } else { + discoverChildBridge(endpoint); + } + } + + private void updateBridgeEndpointMap(Endpoint endpoint, final EndpointHandler handler) { + bridgedEndpoints.put(endpoint.number, handler); + endpoint.children.forEach(e -> updateBridgeEndpointMap(e, handler)); + } + + private void discoverChildBridge(Endpoint endpoint) { + logger.debug("discoverChildBridge {}", endpoint.number); + ControllerHandler controller = controllerHandler(); + if (controller != null) { + MatterDiscoveryService discoveryService = controller.getDiscoveryService(); + if (discoveryService != null) { + ThingUID bridgeUID = getThing().getUID(); + ThingUID thingUID = new ThingUID(THING_TYPE_ENDPOINT, bridgeUID, endpoint.number.toString()); + discoveryService.discoverBridgeEndpoint(thingUID, bridgeUID, endpoint); + } + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterLabelUtils.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterLabelUtils.java new file mode 100644 index 00000000000..20144daee32 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterLabelUtils.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import java.net.Inet6Address; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BaseCluster.OctetString; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BasicInformationCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BridgedDeviceBasicInformationCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DescriptorCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DescriptorCluster.DeviceTypeStruct; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DeviceTypes; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.FixedLabelCluster; + +/** + * Utility class for creating and manipulating the labels for Matter devices. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterLabelUtils { + + public static String normalizeString(@Nullable String input) { + /* + * \\p{C}: Matches control characters (category Cc). + * \\p{Z}: Matches whitespace characters. + * &&[^\\u0020]: Excludes the regular space (\u0020) from removal. + */ + return input == null ? "" : input.replaceAll("[\\p{C}\\p{Z}&&[^\\u0020]]", "").trim(); + } + + public static String labelForNode(Endpoint root) { + String label = ""; + String vendorName = ""; + String productName = ""; + String nodeLabel = ""; + + BaseCluster cluster = root.clusters.get(BasicInformationCluster.CLUSTER_NAME); + if (cluster != null && cluster instanceof BasicInformationCluster basicCluster) { + vendorName = normalizeString(basicCluster.vendorName); + productName = normalizeString(basicCluster.productName); + nodeLabel = normalizeString(basicCluster.nodeLabel); + } + + if (!nodeLabel.isEmpty()) { + label = nodeLabel; + } else { + label = productName.startsWith(vendorName) ? productName : vendorName + " " + productName; + } + + return label.trim(); + } + + public static String labelForEndpoint(Endpoint endpoint) { + Map clusters = endpoint.clusters; + Object basicInfoObject = clusters.get(BasicInformationCluster.CLUSTER_NAME); + + Integer deviceTypeID = primaryDeviceTypeForEndpoint(endpoint); + + // labels will look like "Device Type : Custom Node Label Or Product Label" + final StringBuffer label = new StringBuffer(splitAndCapitalize(DeviceTypes.DEVICE_MAPPING.get(deviceTypeID))) + .append(": "); + + // Check if a "nodeLabel" is set, otherwise use the product label. This varies from vendor to vendor + if (basicInfoObject != null) { + BasicInformationCluster basicInfo = (BasicInformationCluster) basicInfoObject; + String basicInfoString = normalizeString(basicInfo.nodeLabel); + label.append(!basicInfoString.isEmpty() ? basicInfoString : normalizeString(basicInfo.productLabel)); + } + + // Fixed labels are a way of vendors to label endpoints with additional meta data. + if (clusters.get(FixedLabelCluster.CLUSTER_NAME) instanceof FixedLabelCluster fixedLabelCluster) { + fixedLabelCluster.labelList + .forEach(fixedLabel -> label.append(" " + fixedLabel.label + " " + fixedLabel.value)); + } + + // label for the Group Channel + return label.toString().trim(); + } + + public static String labelForBridgeEndpoint(Endpoint endpoint) { + Map clusters = endpoint.clusters; + Object basicInfoObject = clusters.get(BridgedDeviceBasicInformationCluster.CLUSTER_NAME); + + // labels will look like "Device Type : Custom Node Label Or Product Label" + final StringBuffer label = new StringBuffer(); + // Check if a "nodeLabel" is set, otherwise use the product label. This varies from vendor to vendor + if (basicInfoObject != null) { + BridgedDeviceBasicInformationCluster basicInfo = (BridgedDeviceBasicInformationCluster) basicInfoObject; + String nodeLabel = normalizeString(basicInfo.nodeLabel); + String productLabel = normalizeString(basicInfo.productLabel); + + if (!nodeLabel.isEmpty()) { + label.append(nodeLabel); + } else { + label.append(productLabel); + } + } + + if (label.isEmpty()) { + Integer deviceTypeID = primaryDeviceTypeForEndpoint(endpoint); + String deviceTypeLabel = splitAndCapitalize(DeviceTypes.DEVICE_MAPPING.get(deviceTypeID)); + label.append(deviceTypeLabel + " (" + endpoint.number.toString() + ")"); + } + + // Fixed labels are a way of vendors to label endpoints with additional meta data. + if (clusters.get(FixedLabelCluster.CLUSTER_NAME) instanceof FixedLabelCluster fixedLabelCluster) { + fixedLabelCluster.labelList + .forEach(fixedLabel -> label.append(" " + fixedLabel.label + " " + fixedLabel.value)); + } + + // label for the Group Channel + return label.toString().trim(); + } + + public static String splitAndCapitalize(@Nullable String camelCase) { + if (camelCase == null) { + return ""; + } + return Pattern.compile("(?<=[a-z])(?=[A-Z])").splitAsStream(camelCase) + .map(word -> word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase()) + .collect(Collectors.joining(" ")); + } + + public static Integer primaryDeviceTypeForEndpoint(Endpoint endpoint) { + // The matter spec requires a descriptor cluster and device type, so this should always be present + DescriptorCluster descriptorCluster = (DescriptorCluster) endpoint.clusters.get(DescriptorCluster.CLUSTER_NAME); + Integer deviceTypeID = -1; + if (descriptorCluster != null && !descriptorCluster.deviceTypeList.isEmpty()) { + for (DeviceTypeStruct ds : descriptorCluster.deviceTypeList) { + // ignore bridge types + if (!DeviceTypes.BRIDGED_NODE.equals(ds.deviceType) && !DeviceTypes.AGGREGATOR.equals(ds.deviceType)) { + deviceTypeID = ds.deviceType; + break; + } + } + if (deviceTypeID == -1) { + deviceTypeID = descriptorCluster.deviceTypeList.get(0).deviceType; + } + } + return deviceTypeID; + } + + public static String formatMacAddress(@Nullable OctetString mac) { + if (mac == null) { + return ""; + } + String macString = mac.toString(); + return IntStream.range(0, macString.length()).filter(i -> i % 2 == 0) + .mapToObj(i -> macString.substring(i, i + 2)).collect(Collectors.joining(":")); + } + + public static String formatIPv4Address(@Nullable OctetString os) { + if (os == null || os.value == null || os.value.length != 4) { + return ""; + } + + byte[] addr = os.value; + return (addr[0] & 0xFF) + "." + (addr[1] & 0xFF) + "." + (addr[2] & 0xFF) + "." + (addr[3] & 0xFF); + } + + public static String formatIPv6Address(@Nullable OctetString os) { + if (os == null || os.value == null || os.value.length != 16) { + return ""; + } + + try { + // passing in host=null ensures this call does not block or throw + Inet6Address ip = (Inet6Address) Inet6Address.getByAddress(null, os.value, -1); + return ip.getHostAddress(); + } catch (UnknownHostException e) { + return os.toHexString(); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterUIDUtils.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterUIDUtils.java new file mode 100644 index 00000000000..ba2113e3495 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterUIDUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterBindingConstants; +import org.openhab.core.thing.ThingTypeUID; + +/** + * Utility class for creating and manipulating the UIDs for Matter thing types. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterUIDUtils { + + /** + * Node thing types will have a UUID like matter:node_1234567890 + * + * @param nodeId the node ID of the Node + * @return a ThingTypeUID for a Node + */ + public static ThingTypeUID nodeThingTypeUID(BigInteger nodeId) { + return new ThingTypeUID(MatterBindingConstants.BINDING_ID, + MatterBindingConstants.THING_TYPE_NODE.getId() + "-" + nodeId); + } + + /** + * Bridge Endpoint thing types will have a UUID like matter matter:endpoint_1234567890_1 + * + * @param nodeId the node ID of the Node + * @param endpointNumber the endpoint number of the Bridge Endpoint + * @return a ThingTypeUID for a Bridge Endpoint + */ + public static ThingTypeUID endpointThingTypeUID(BigInteger nodeId, Integer endpointNumber) { + return new ThingTypeUID(MatterBindingConstants.BINDING_ID, + MatterBindingConstants.THING_TYPE_ENDPOINT.getId() + "-" + nodeId + "-" + endpointNumber); + } + + /** + * Returns the base Thing type (node, etc...) for a dynamic thing + * + * @param thingTypeUID the ThingTypeUID to get the base type for + * @return the base ThingTypeUID + */ + public static @Nullable ThingTypeUID baseTypeForThingType(ThingTypeUID thingTypeUID) { + String type = thingTypeUID.getId().split("-", 2)[0]; + switch (type) { + case "node": + return MatterBindingConstants.THING_TYPE_NODE; + case "endpoint": + return MatterBindingConstants.THING_TYPE_ENDPOINT; + default: + return null; + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterVendorIDs.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterVendorIDs.java new file mode 100644 index 00000000000..48a9f3e1a49 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/MatterVendorIDs.java @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Utility class for the vendor IDs for Matter devices. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class MatterVendorIDs { + + /** + * This list generated from https://github.com/ioBroker/ioBroker.matter/blob/main/src-admin/src/utils/vendorIDs.ts + */ + public static final Map VENDOR_IDS = Map.ofEntries(Map.entry(0x0005, "Freescale (RF4CE)"), + Map.entry(0x0006, "Oki Semiconductors (RF4CE)"), Map.entry(0x0007, "Texas Instruments"), + Map.entry(0x1000, "Cirronet"), Map.entry(0x1001, "Chipcon"), Map.entry(0x1002, "Ember"), + Map.entry(0x1003, "NTS"), Map.entry(0x1004, "Freescale"), Map.entry(0x1005, "IP Com"), + Map.entry(0x1006, "San Juan Software"), Map.entry(0x1007, "TUV"), Map.entry(0x1008, "Integration"), + Map.entry(0x1009, "BM SpA"), Map.entry(0x100a, "AwarePoint"), Map.entry(0x100b, "Signify Netherlands B.V."), + Map.entry(0x100c, "Luxoft"), Map.entry(0x100d, "Korwin"), Map.entry(0x100e, "One RF Technology"), + Map.entry(0x100f, "Software Technologies Group"), Map.entry(0x1010, "Telegesis"), + Map.entry(0x1011, "Visonic"), Map.entry(0x1012, "Insta"), Map.entry(0x1013, "Atalum"), + Map.entry(0x1014, "Atmel"), Map.entry(0x1015, "Develco"), Map.entry(0x1016, "Honeywell"), + Map.entry(0x1017, "RadioPulse"), Map.entry(0x1018, "Renesas"), Map.entry(0x1019, "Xanadu Wireless"), + Map.entry(0x101a, "NEC Engineering"), Map.entry(0x101b, "Yamatake Corporation"), + Map.entry(0x101c, "Tendril Networks"), Map.entry(0x101d, "Assa Abloy"), Map.entry(0x101e, "MaxStream"), + Map.entry(0x101f, "Neurocom"), Map.entry(0x1020, "Institute for Information Industry"), + Map.entry(0x1021, "Legrand Group"), Map.entry(0x1022, "iControl"), Map.entry(0x1023, "Raymarine"), + Map.entry(0x1024, "LS Research"), Map.entry(0x1025, "Onity Inc."), Map.entry(0x1026, "Mono Products"), + Map.entry(0x1027, "RF Technologies"), Map.entry(0x1028, "Itron"), Map.entry(0x1029, "Tritech"), + Map.entry(0x102a, "Embedit A/S"), Map.entry(0x102b, "S3C"), Map.entry(0x102c, "Siemens"), + Map.entry(0x102d, "Mindtech"), Map.entry(0x102e, "LG Electronics"), + Map.entry(0x102f, "Mitsubishi Electric Corp."), Map.entry(0x1030, "Johnson Controls"), + Map.entry(0x1031, "Secure Meters (UK) Ltd"), Map.entry(0x1032, "Knick"), Map.entry(0x1033, "Viconics"), + Map.entry(0x1034, "Flexipanel"), Map.entry(0x1035, "Piasim Corporation Pte., Ltd."), + Map.entry(0x1036, "Trane"), Map.entry(0x1037, "NXP Semiconductors"), + Map.entry(0x1038, "Living Independently Group"), Map.entry(0x1039, "AlertMe.com"), + Map.entry(0x103a, "Daintree"), Map.entry(0x103b, "Aiji System"), Map.entry(0x103c, "Telecom Italia"), + Map.entry(0x103d, "Mikrokrets AS"), Map.entry(0x103e, "Oki Semiconductor"), + Map.entry(0x103f, "Newport Electonics"), Map.entry(0x1040, "Control 4"), + Map.entry(0x1041, "STMicroelectronics"), Map.entry(0x1042, "Ad-Sol Nissin Corp"), Map.entry(0x1043, "DCSI"), + Map.entry(0x1044, "France Telecom"), Map.entry(0x1045, "muNet"), Map.entry(0x1046, "Autani Corporation"), + Map.entry(0x1047, "Colorado vNet"), Map.entry(0x1048, "Aerocomm, Inc."), + Map.entry(0x1049, "Silicon Laboratories"), Map.entry(0x104a, "Inncom International Inc."), + Map.entry(0x104b, "Cooper Power Systems"), Map.entry(0x104c, "Synapse"), + Map.entry(0x104d, "Fisher Pierce/Sunrise"), Map.entry(0x104e, "CentraLite Systems, Inc."), + Map.entry(0x104f, "Crane Wireless Monitoring Solutions"), Map.entry(0x1050, "Mobilarm Limited"), + Map.entry(0x1051, "iMonitor Research Ltd."), Map.entry(0x1052, "Bartech"), Map.entry(0x1053, "MeshNetics"), + Map.entry(0x1054, "LS Industrial Systems Co. Ltd."), Map.entry(0x1055, "Cason Engineering plc"), + Map.entry(0x1056, "Wireless Glue Networks Inc."), Map.entry(0x1057, "Elster"), + Map.entry(0x1058, "SMS Tecnologia Eletrônica"), Map.entry(0x1059, "Onset Computer Corporation"), + Map.entry(0x105a, "Riga Development"), Map.entry(0x105b, "Energate"), Map.entry(0x105c, "ConMed Linvatec"), + Map.entry(0x105d, "PowerMand"), Map.entry(0x105e, "Schneider Electric"), + Map.entry(0x105f, "Eaton Corporation"), Map.entry(0x1060, "Telular Corporation"), + Map.entry(0x1061, "Delphi Medical Systems"), Map.entry(0x1062, "EpiSensor Limited"), + Map.entry(0x1063, "Landis+Gyr"), Map.entry(0x1064, "Kaba Group"), Map.entry(0x1065, "Shure Incorporated"), + Map.entry(0x1066, "Comverge, Inc."), Map.entry(0x1067, "DBS Lodging Technologies, LLC."), + Map.entry(0x1068, "Energy Aware Technology Inc."), Map.entry(0x1069, "Hidalgo Limited"), + Map.entry(0x106a, "Air2App"), Map.entry(0x106b, "AMX"), Map.entry(0x106c, "EDMI Pty Ltd"), + Map.entry(0x106d, "Cyan Ltd"), Map.entry(0x106e, "System SPA"), Map.entry(0x106f, "Telit"), + Map.entry(0x1070, "Kaga Electronics"), Map.entry(0x1071, "Astrel Group SRL"), Map.entry(0x1072, "Certicom"), + Map.entry(0x1073, "Gridpoint"), Map.entry(0x1074, "Profile Systems LLC"), + Map.entry(0x1075, "Compacta International Ltd"), Map.entry(0x1076, "Freestyle Technology Pty Ltd."), + Map.entry(0x1077, "Alektrona"), Map.entry(0x1078, "Computime"), + Map.entry(0x1079, "Remote Technologies, Inc."), Map.entry(0x107a, "Wavecom S.A."), + Map.entry(0x107b, "Energy Optimizers Ltd."), Map.entry(0x107c, "GE"), Map.entry(0x107d, "Jetlun"), + Map.entry(0x107e, "Cipher Systems"), Map.entry(0x107f, "Corporate Systems Engineering"), + Map.entry(0x1080, "ecobee"), Map.entry(0x1081, "SMK"), Map.entry(0x1082, "Meshworks Wireless Oy"), + Map.entry(0x1083, "Ellips B.V."), Map.entry(0x1084, "Secure electrans"), Map.entry(0x1085, "CEDO"), + Map.entry(0x1086, "Toshiba"), Map.entry(0x1087, "Digi International"), Map.entry(0x1088, "Ubilogix"), + Map.entry(0x1089, "Echelon"), Map.entry(0x1090, "Green Energy Options"), + Map.entry(0x1091, "Silver Spring Networks"), Map.entry(0x1092, "Black & Decker"), + Map.entry(0x1093, "Aztech Associates Inc."), Map.entry(0x1094, "A&D Co., Ltd."), + Map.entry(0x1095, "Rainforest Automation"), Map.entry(0x1096, "Carrier Electronics"), + Map.entry(0x1097, "SyChip/Murata"), Map.entry(0x1098, "OpenPeak"), Map.entry(0x1099, "PassiveSystems"), + Map.entry(0x109a, "MMB Research"), Map.entry(0x109b, "Leviton Manufacturing Company"), + Map.entry(0x109c, "Korea Electric Power Data Network Co., Ltd."), Map.entry(0x109d, "Comcast"), + Map.entry(0x109e, "NEC Electronics"), Map.entry(0x109f, "Netvox"), Map.entry(0x10a0, "U-Control"), + Map.entry(0x10a1, "Embedia Technologies Corp"), Map.entry(0x10a2, "Sensus"), + Map.entry(0x10a3, "Sunrise Technologies"), Map.entry(0x10a4, "Memtech Corp"), Map.entry(0x10a5, "Freebox"), + Map.entry(0x10a6, "M2 Labs Ltd."), Map.entry(0x10a7, "British Gas"), Map.entry(0x10a8, "Sentec Ltd."), + Map.entry(0x10a9, "Navetas"), Map.entry(0x10aa, "Lightspeed Technologies"), + Map.entry(0x10ab, "Oki Electric Industry Co., Ltd."), + Map.entry(0x10ac, "S I - Sistemas Inteligentes Eletrônicos Ltda"), Map.entry(0x10ad, "Dometic"), + Map.entry(0x10ae, "Alps"), Map.entry(0x10af, "EnergyHub"), Map.entry(0x10b0, "Kamstrup"), + Map.entry(0x10b1, "EchoStar"), Map.entry(0x10b2, "EnerNOC"), Map.entry(0x10b3, "Eltav"), + Map.entry(0x10b4, "Belkin"), Map.entry(0x10b5, "XStreamHD - Wireless Ventures"), + Map.entry(0x10b6, "Saturn South Pty Ltd"), Map.entry(0x10b7, "GreenTrapOnline A/S"), + Map.entry(0x10b8, "SmartSynch, Inc."), Map.entry(0x10b9, "Nyce Control, Inc."), + Map.entry(0x10ba, "ICM Controls Corp"), Map.entry(0x10bb, "Millennium Electronics, PTY Ltd."), + Map.entry(0x10bc, "Motorola, Inc"), Map.entry(0x10bd, "Emerson White-Rodgers"), + Map.entry(0x10be, "Radio Thermostat Company of America"), Map.entry(0x10bf, "OMRON Corporation"), + Map.entry(0x10c0, "GiiNii Global Limited"), Map.entry(0x10c1, "Fujitsu General Limited"), + Map.entry(0x10c2, "Peel Technologies, Inc."), Map.entry(0x10c3, "Accent S.p.A."), + Map.entry(0x10c4, "ByteSnap Design Ltd."), Map.entry(0x10c5, "NEC TOKIN Corporation"), + Map.entry(0x10c6, "G4S Justice Services"), Map.entry(0x10c7, "Trilliant Networks, Inc."), + Map.entry(0x10c8, "Electrolux Italia S.p.A"), Map.entry(0x10c9, "Onzo Ltd"), + Map.entry(0x10ca, "EnTek Systems"), Map.entry(0x10cb, "Philips"), + Map.entry(0x10cc, "Mainstream Engineering"), Map.entry(0x10cd, "Indesit Company"), + Map.entry(0x10ce, "THINKECO, INC."), Map.entry(0x10cf, "2D2C, Inc."), Map.entry(0x10d0, "Qorvo"), + Map.entry(0x10d1, "InterCEL"), Map.entry(0x10d2, "LG Electronics"), + Map.entry(0x10d3, "Mitsumi Electric Co.,Ltd."), Map.entry(0x10d4, "Mitsumi Electric Co.,Ltd."), + Map.entry(0x10d5, "Zentrum Mikroelektronik Dresden AG (ZMDI)"), Map.entry(0x10d6, "Nest Labs, Inc."), + Map.entry(0x10d7, "Exegin Technologies, Ltd."), Map.entry(0x10d8, "Honeywell"), + Map.entry(0x10d9, "Takahata Precision Co. Ltd."), Map.entry(0x10da, "SUMITOMO ELECTRIC NETWORKS, INC."), + Map.entry(0x10db, "GE Energy"), Map.entry(0x10dc, "GE Appliances"), Map.entry(0x10dd, "Radiocrafts AS"), + Map.entry(0x10de, "Ceiva"), Map.entry(0x10df, "TEC&CO Co., Ltd"), + Map.entry(0x10e0, "Chameleon Technology (UK) Ltd"), Map.entry(0x10e1, "Samsung"), + Map.entry(0x10e2, "ruwido austria gmbh"), Map.entry(0x10e3, "Huawei Technologies Co., Ltd."), + Map.entry(0x10e4, "Huawei Technologies Co., Ltd."), Map.entry(0x10e5, "Greenwave Reality"), + Map.entry(0x10e6, "BGlobal Metering Ltd"), Map.entry(0x10e7, "Mindteck"), + Map.entry(0x10e8, "Ingersoll-Rand"), Map.entry(0x10e9, "Dius Computing Pty Ltd"), + Map.entry(0x10ea, "Embedded Automation, Inc."), Map.entry(0x10eb, "ABB"), Map.entry(0x10ec, "Sony"), + Map.entry(0x10ed, "Genus Power Infrastructures Limited"), Map.entry(0x10ee, "Universal Electronics, Inc."), + Map.entry(0x10ef, "Universal Electronics, Inc."), Map.entry(0x10f0, "Metrum Technologies, LLC"), + Map.entry(0x10f1, "Cisco"), Map.entry(0x10f2, "Ubisys technologies GmbH"), Map.entry(0x10f3, "Consert"), + Map.entry(0x10f4, "Crestron Electronics"), Map.entry(0x10f5, "Enphase Energy"), + Map.entry(0x10f6, "Invensys Controls"), Map.entry(0x10f7, "Mueller Systems, LLC"), + Map.entry(0x10f8, "AAC Technologies Holding"), Map.entry(0x10f9, "U-NEXT Co., Ltd"), + Map.entry(0x10fa, "Steelcase Inc."), Map.entry(0x10fb, "Telematics Wireless"), + Map.entry(0x10fc, "Samil Power Co., Ltd"), Map.entry(0x10fd, "Pace Plc"), + Map.entry(0x10fe, "Osborne Coinage Co."), Map.entry(0x10ff, "Powerwatch"), + Map.entry(0x1100, "CANDELED GmbH"), Map.entry(0x1101, "FlexGrid S.R.L"), Map.entry(0x1102, "Humax"), + Map.entry(0x1103, "Universal Devices"), Map.entry(0x1104, "Advanced Energy"), + Map.entry(0x1105, "BEGA Gantenbrink-Leuchten"), Map.entry(0x1106, "Brunel University"), + Map.entry(0x1107, "Panasonic R&D Center Singapore"), Map.entry(0x1108, "eSystems Research"), + Map.entry(0x1109, "Panamax"), Map.entry(0x110a, "SmartThings, Inc."), Map.entry(0x110b, "EM-Lite Ltd."), + Map.entry(0x110c, "Osram Sylvania"), Map.entry(0x110d, "2 Save Energy Ltd."), + Map.entry(0x110e, "Planet Innovation Products Pty Ltd"), Map.entry(0x110f, "Ambient Devices, Inc."), + Map.entry(0x1110, "Profalux"), Map.entry(0x1111, "Billion Electric Company (BEC)"), + Map.entry(0x1112, "Embertec Pty Ltd"), Map.entry(0x1113, "IT Watchdogs"), Map.entry(0x1114, "Reloc"), + Map.entry(0x1115, "Intel Corporation"), Map.entry(0x1116, "Trend Electronics Limited"), + Map.entry(0x1117, "Moxa"), Map.entry(0x1118, "QEES"), Map.entry(0x1119, "SAYME Wireless Sensor Networks"), + Map.entry(0x111a, "Pentair Aquatic Systems"), Map.entry(0x111b, "Orbit Irrigation"), + Map.entry(0x111c, "California Eastern Laboratories"), Map.entry(0x111d, "Comcast"), + Map.entry(0x111e, "IDT Technology Limited"), Map.entry(0x111f, "Pixela Corporation"), + Map.entry(0x1120, "TiVo, Inc."), Map.entry(0x1121, "Fidure Corp."), + Map.entry(0x1122, "Marvell Semiconductor, Inc."), Map.entry(0x1123, "Wasion Group Limited"), + Map.entry(0x1124, "Jasco Products Company"), + Map.entry(0x1125, "Shenzhen Kaifa Technology (Chengdu) Co., Ltd."), + Map.entry(0x1126, "Netcomm Wireless Limited"), Map.entry(0x1127, "Define Instruments Limited"), + Map.entry(0x1128, "In Home Displays Ltd."), Map.entry(0x1129, "Miele & Cie. KG"), + Map.entry(0x112a, "Televes S.A."), Map.entry(0x112b, "Labelec"), + Map.entry(0x112c, "China Electronics Standardization Institute"), Map.entry(0x112d, "Vectorform, LLC"), + Map.entry(0x112e, "Busch-Jaeger Elektro"), Map.entry(0x112f, "Redpine Signals, Inc."), + Map.entry(0x1130, "Bridges Electronic Technology Pty Ltd."), Map.entry(0x1131, "Sercomm"), + Map.entry(0x1132, "WSH GmbH wirsindheller"), Map.entry(0x1133, "Bosch Security Systems, Inc."), + Map.entry(0x1134, "eZEX Corporation"), Map.entry(0x1135, "Dresden Elektronik Ingenieurtechnik GmbH"), + Map.entry(0x1136, "MEAZON S.A."), Map.entry(0x1137, "Crow Electronic Engineering Ltd."), + Map.entry(0x1138, "Harvard Engineering Plc"), Map.entry(0x1139, "Andson(Beijing) Technology CO.,Ltd"), + Map.entry(0x113a, "Adhoco AG"), Map.entry(0x113b, "Waxman Consumer Products Group, Inc."), + Map.entry(0x113c, "Owon Technology, Inc."), Map.entry(0x113d, "Hitron Technologies, Inc."), + Map.entry(0x113e, "Scemtec Hard - und Software für Mess - und Steuerungstechnik GmbH"), + Map.entry(0x113f, "Webee LLC"), Map.entry(0x1140, "Grid2Home Inc"), Map.entry(0x1141, "Telink Micro"), + Map.entry(0x1142, "Jasmine Systems, Inc."), Map.entry(0x1143, "Bidgely"), Map.entry(0x1144, "Lutron"), + Map.entry(0x1145, "IJENKO"), Map.entry(0x1146, "Starfield Electronic Ltd."), Map.entry(0x1147, "TCP, Inc."), + Map.entry(0x1148, "Rogers Communications Partnership"), Map.entry(0x1149, "Cree, Inc."), + Map.entry(0x114a, "Robert Bosch LLC"), Map.entry(0x114b, "Ibis Networks, Inc."), + Map.entry(0x114c, "Quirky, Inc."), Map.entry(0x114d, "Efergy Technologies Limited"), + Map.entry(0x114e, "SmartLabs, Inc."), Map.entry(0x114f, "Everspring Industry Co., Ltd."), + Map.entry(0x1150, "Swann Communications Ptl Ltd."), Map.entry(0x1151, "Soneter"), + Map.entry(0x1152, "Samsung SDS"), Map.entry(0x1153, "Uniband Electronic Corporation"), + Map.entry(0x1154, "Accton Technology Corporation"), Map.entry(0x1155, "Bosch Thermotechnik GmbH"), + Map.entry(0x1156, "Wincor Nixdorf Inc."), Map.entry(0x1157, "Ohsung Electronics"), + Map.entry(0x1158, "Zen Within, Inc."), Map.entry(0x1159, "Tech4home, Lda."), Map.entry(0x115a, "Nanoleaf"), + Map.entry(0x115b, "Keen Home, Inc."), Map.entry(0x115c, "Poly-Control APS"), + Map.entry(0x115d, "Eastfield Lighting Co., Ltd Shenzhen"), Map.entry(0x115e, "IP Datatel, Inc."), + Map.entry(0x115f, "Lumi United Techology, Ltd Shenzhen"), Map.entry(0x1160, "Sengled Co., Ltd."), + Map.entry(0x1161, "Remote Solution Co., Ltd."), + Map.entry(0x1162, "ABB Genway Xiamen Electrical Equipment Co., Ltd."), + Map.entry(0x1163, "Zhejiang Rexense Tech"), Map.entry(0x1164, "ForEE Technology"), + Map.entry(0x1165, "Open Access Technology Int’l."), Map.entry(0x1166, "INNR Lighting BV"), + Map.entry(0x1167, "Techworld Industries"), Map.entry(0x1168, "Leedarson Lighting Co., Ltd."), + Map.entry(0x1169, "Arzel Zoning"), Map.entry(0x116a, "Holley Technology"), + Map.entry(0x116b, "Beldon Technologies"), Map.entry(0x116c, "Flextronics"), + Map.entry(0x116d, "Shenzhen Meian"), Map.entry(0x116e, "Lowe’s"), + Map.entry(0x116f, "Sigma Connectivity"), Map.entry(0x1171, "Wulian"), Map.entry(0x1172, "Plugwise B.V."), + Map.entry(0x1173, "Titan Products"), Map.entry(0x1174, "Ecospectral"), Map.entry(0x1175, "D-Link"), + Map.entry(0x1176, "Technicolor Home USA"), Map.entry(0x1177, "Opple Lighting"), + Map.entry(0x1178, "Wistron NeWeb Corp."), Map.entry(0x1179, "QMotion Shades"), + Map.entry(0x117a, "Insta GmbH"), Map.entry(0x117b, "Shanghai Vancount"), + Map.entry(0x117c, "Ikea of Sweden"), Map.entry(0x117d, "RT-RK"), Map.entry(0x117e, "Shenzhen Feibit"), + Map.entry(0x117f, "EuControls"), Map.entry(0x1180, "Telkonet"), + Map.entry(0x1181, "Thermal Solution Resources"), Map.entry(0x1182, "PomCube"), + Map.entry(0x1183, "Ei Electronics"), Map.entry(0x1184, "Optoga"), Map.entry(0x1185, "Stelpro"), + Map.entry(0x1186, "Lynxus Technologies Corp."), Map.entry(0x1187, "Semiconductor Components"), + Map.entry(0x1188, "TP-Link"), Map.entry(0x1189, "Ledvance GmbH"), Map.entry(0x118a, "Nortek"), + Map.entry(0x118b, "iRevo/Assa Abbloy Korea"), Map.entry(0x118c, "Midea"), + Map.entry(0x118d, "ZF Friedrichshafen"), Map.entry(0x118e, "Checkit"), Map.entry(0x118f, "Aclara"), + Map.entry(0x1190, "Nokia"), Map.entry(0x1191, "Goldcard High-tech Co., Ltd."), + Map.entry(0x1192, "George Wilson Industries Ltd."), Map.entry(0x1193, "EASY SAVER CO.,INC"), + Map.entry(0x1194, "ZTE Corporation"), Map.entry(0x1195, "ARRIS"), Map.entry(0x1196, "Reliance BIG TV"), + Map.entry(0x1197, "Insight Energy Ventures/Powerley"), + Map.entry(0x1198, "Thomas Research Products (Hubbell Lighting Inc.)"), + Map.entry(0x1199, "Li Seng Technology"), Map.entry(0x119a, "System Level Solutions Inc."), + Map.entry(0x119b, "Matrix Labs"), Map.entry(0x119c, "Sinope Technologies"), + Map.entry(0x119d, "Jiuzhou Greeble"), Map.entry(0x119e, "Guangzhou Lanvee Tech. Co. Ltd."), + Map.entry(0x119f, "Venstar"), Map.entry(0x1200, "SLV"), Map.entry(0x1201, "Halo Smart Labs"), + Map.entry(0x1202, "Scout Security Inc."), Map.entry(0x1203, "Alibaba China Inc."), + Map.entry(0x1204, "Resolution Products, Inc."), Map.entry(0x1205, "Smartlok Inc."), + Map.entry(0x1206, "Lux Products Corp."), Map.entry(0x1207, "Vimar SpA"), + Map.entry(0x1208, "Universal Lighting Technologies"), Map.entry(0x1209, "Robert Bosch, GmbH"), + Map.entry(0x120a, "Accenture"), Map.entry(0x120b, "Heiman Technology Co., Ltd."), + Map.entry(0x120c, "Shenzhen HOMA Technology Co., Ltd."), Map.entry(0x120d, "Vision-Electronics Technology"), + Map.entry(0x120e, "Lenovo"), Map.entry(0x120f, "Presciense R&D"), + Map.entry(0x1210, "Shenzhen Seastar Intelligence Co., Ltd."), Map.entry(0x1211, "Sensative AB"), + Map.entry(0x1212, "SolarEdge"), Map.entry(0x1213, "Zipato"), + Map.entry(0x1214, "China Fire & Security Sensing Manufacturing (iHorn)"), Map.entry(0x1215, "Quby BV"), + Map.entry(0x1216, "Hangzhou Roombanker Technology Co., Ltd."), Map.entry(0x1217, "Amazon Lab126"), + Map.entry(0x1218, "Paulmann Licht GmbH"), Map.entry(0x1219, "Shenzhen Orvibo Electronics Co. Ltd."), + Map.entry(0x121a, "TCI Telecommunications"), Map.entry(0x121b, "Mueller-Licht International Inc."), + Map.entry(0x121c, "Aurora Limited"), Map.entry(0x121d, "SmartDCC"), + Map.entry(0x121e, "Shanghai UMEinfo Co. Ltd."), Map.entry(0x121f, "carbonTRACK"), + Map.entry(0x1220, "Somfy"), Map.entry(0x1221, "Viessmann Elektronik GmbH"), + Map.entry(0x1222, "Hildebrand Technology Ltd"), Map.entry(0x1223, "Onkyo Technology Corporation"), + Map.entry(0x1224, "Shenzhen Sunricher Technology Ltd."), Map.entry(0x1225, "Xiu Xiu Technology Co., Ltd"), + Map.entry(0x1226, "Zumtobel Group"), Map.entry(0x1227, "Shenzhen Kaadas Intelligent Technology Co. Ltd"), + Map.entry(0x1228, "Shanghai Xiaoyan Technology Co. Ltd"), Map.entry(0x1229, "Cypress Semiconductor"), + Map.entry(0x122a, "XAL GmbH"), Map.entry(0x122b, "Inergy Systems LLC"), + Map.entry(0x122c, "Alfred Karcher GmbH & Co KG"), Map.entry(0x122d, "Adurolight Manufacturing"), + Map.entry(0x122e, "Groupe Muller"), Map.entry(0x122f, "V-Mark Enterprises Inc."), + Map.entry(0x1230, "Lead Energy AG"), Map.entry(0x1231, "Ultimate IOT (Henan) Technology Ltd."), + Map.entry(0x1232, "Axxess Industries Inc."), Map.entry(0x1233, "Third Reality Inc."), + Map.entry(0x1234, "DSR Corporation"), Map.entry(0x1235, "Guangzhou Vensi Intelligent Technology Co. Ltd."), + Map.entry(0x1236, "Schlage Lock (Allegion)"), Map.entry(0x1237, "Net2Grid"), + Map.entry(0x1238, "Airam Electric Oy Ab"), Map.entry(0x1239, "IMMAX WPB CZ"), + Map.entry(0x123a, "ZIV Automation"), Map.entry(0x123b, "HangZhou iMagicTechnology Co., Ltd"), + Map.entry(0x123c, "Xiamen Leelen Technology Co. Ltd."), Map.entry(0x123d, "Overkiz SAS"), + Map.entry(0x123e, "Flonidan A/S"), Map.entry(0x123f, "HDL Automation Co., Ltd."), + Map.entry(0x1240, "Ardomus Networks Corporation"), Map.entry(0x1241, "Samjin Co., Ltd."), + Map.entry(0x1242, "FireAngel Safety Technology plc"), Map.entry(0x1243, "Indra Sistemas, S.A."), + Map.entry(0x1244, "Shenzhen JBT Smart Lighting Co., Ltd."), Map.entry(0x1245, "GE Lighting & Current"), + Map.entry(0x1246, "Danfoss A/S"), Map.entry(0x1247, "NIVISS PHP Sp. z o.o. Sp.k."), + Map.entry(0x1248, "Shenzhen Fengliyuan Energy Conservating Technology Co. Ltd"), + Map.entry(0x1249, "NEXELEC"), Map.entry(0x124a, "Sichuan Behome Prominent Technology Co., Ltd"), + Map.entry(0x124b, "Fujian Star-net Communication Co., Ltd."), + Map.entry(0x124c, "Toshiba Visual Solutions Corporation"), Map.entry(0x124d, "Latchable, Inc."), + Map.entry(0x124e, "L&S Deutschland GmbH"), Map.entry(0x124f, "Gledopto Co., Ltd."), + Map.entry(0x1250, "The Home Depot"), Map.entry(0x1251, "Neonlite Distribution Limited"), + Map.entry(0x1252, "Arlo Technologies, Inc."), Map.entry(0x1253, "Xingluo Technology Co., Ltd."), + Map.entry(0x1254, "Simon Electric (China) Co., Ltd."), + Map.entry(0x1255, "Hangzhou Greatstar Industrial Co., Ltd."), + Map.entry(0x1256, "Sequentric Energy Systems, LLC"), Map.entry(0x1257, "Solum Co., Ltd."), + Map.entry(0x1258, "Eaglerise Electric & Electronic (China) Co., Ltd."), + Map.entry(0x1259, "Fantem Technologies (Shenzhen) Co., Ltd."), + Map.entry(0x125a, "Yunding Network Technology (Beijing) Co., Ltd."), Map.entry(0x125b, "Atlantic Group"), + Map.entry(0x125c, "Xiamen Intretech, Inc."), Map.entry(0x125d, "Tuya Global Inc."), + Map.entry(0x125e, "Dnake (Xiamen) Intelligent Technology Co., Ltd."), Map.entry(0x125f, "Niko nv"), + Map.entry(0x1260, "Emporia Energy"), Map.entry(0x1261, "Sikom AS"), Map.entry(0x1262, "AXIS Labs, Inc."), + Map.entry(0x1263, "Current Products Corporation"), Map.entry(0x1264, "MeteRSit SRL"), + Map.entry(0x1265, "HORNBACH Baumarkt AG"), Map.entry(0x1266, "DiCEworld s.r.l. a socio unico"), + Map.entry(0x1267, "ARC Technology Co., Ltd"), + Map.entry(0x1268, "Hangzhou Konke Information Technology Co., Ltd."), + Map.entry(0x1269, "SALTO Systems S.L."), Map.entry(0x126a, "Shenzhen Shyugj Technology Co., Ltd"), + Map.entry(0x126b, "Brayden Automation Corporation"), Map.entry(0x126c, "Environexus Pty. Ltd."), + Map.entry(0x126d, "Eltra nv/sa"), Map.entry(0x126e, "Xiaomi Communications Co., Ltd."), + Map.entry(0x126f, "Shanghai Shuncom Electronic Technology Co., Ltd."), Map.entry(0x1270, "Voltalis S.A"), + Map.entry(0x1271, "FEELUX Co., Ltd."), Map.entry(0x1272, "SmartPlus Inc."), + Map.entry(0x1273, "Halemeier GmbH"), Map.entry(0x1274, "Trust International BV"), + Map.entry(0x1275, "Duke Energy Business Services LLC"), Map.entry(0x1276, "Calix, Inc."), + Map.entry(0x1277, "ADEO"), Map.entry(0x1278, "Connected Response Limited"), + Map.entry(0x1279, "StroyEnergoKom, Ltd."), Map.entry(0x127a, "Lumitech Lighting Solution GmbH"), + Map.entry(0x127b, "Verdant Environmental Technologies"), Map.entry(0x127c, "Alfred International Inc."), + Map.entry(0x127d, "Sansi LED Lighting co., LTD."), Map.entry(0x127e, "Mindtree Limited"), + Map.entry(0x127f, "Nordic Semiconductor ASA"), Map.entry(0x1280, "Siterwell Electronics Co., Limited"), + Map.entry(0x1281, "Briloner Leuchten GmbH & Co. KG"), + Map.entry(0x1282, "Shenzhen SEI Technology Co., Ltd."), Map.entry(0x1283, "Copper Labs, Inc."), + Map.entry(0x1284, "Delta Dore"), Map.entry(0x1285, "Hager Group"), + Map.entry(0x1286, "Shenzhen CoolKit Technology Co., Ltd"), + Map.entry(0x1287, "Hangzhou Sky-Lighting Co., Ltd."), Map.entry(0x1288, "E.ON SE"), + Map.entry(0x1289, "Lidl Stiftung & Co. KG"), + Map.entry(0x128a, "Sichuan Changhong Network Technologies Co., Ltd."), Map.entry(0x128b, "NodOn"), + Map.entry(0x128c, "Jiangxi Innotech Technology Co., Ltd."), Map.entry(0x128d, "Mercator Pty Ltd"), + Map.entry(0x128e, "Beijing Ruying Tech Limited"), Map.entry(0x128f, "EGLO Leuchten GmbH"), + Map.entry(0x1290, "Pietro Fiorentini S.p.A"), Map.entry(0x1291, "Zehnder Group Vaux-Andigny"), + Map.entry(0x1292, "BRK Brands, Inc."), Map.entry(0x1293, "Askey Computer Corp."), + Map.entry(0x1294, "PassiveBolt, Inc."), + Map.entry(0x1295, "AVM Audiovisuelles Marketing und Computersysteme Berlin"), + Map.entry(0x1296, "Ningbo Suntech Lighting Technology Co., Ltd"), + Map.entry(0x1297, "Société en Commandite Stello"), Map.entry(0x1298, "Vivint Smart Home"), + Map.entry(0x1299, "Namron AS"), Map.entry(0x129a, "RADEMACHER Geraete-Elektronik GmbH"), + Map.entry(0x129b, "OMO Systems LTD"), Map.entry(0x129c, "Siglis AG"), Map.entry(0x129d, "IMHOTEP CREATION"), + Map.entry(0x129e, "icasa"), Map.entry(0x129f, "Level Home, Inc."), Map.entry(0x1300, "TIS Control Limited"), + Map.entry(0x1301, "Radisys India Pvt. Ltd."), Map.entry(0x1302, "Veea Inc."), + Map.entry(0x1303, "FELL Technology AS"), Map.entry(0x1304, "Sowilo Design Services, Ltd."), + Map.entry(0x1305, "Lexi Devices, Inc."), Map.entry(0x1306, "Lifi Labs INC. dba LIFX"), + Map.entry(0x1307, "GRUNDFOS Holding A/S"), Map.entry(0x1308, "SOURCING & CREATION"), + Map.entry(0x1309, "Kraken Technologies Ltd"), Map.entry(0x130a, "EVE SYSTEMS"), + Map.entry(0x130b, "LITE-ON TECHNOLOGY CORPORATION"), Map.entry(0x130c, "Focalcrest Limited"), + Map.entry(0x130d, "Bouffalo Lab (Nanjing) Co., Ltd."), Map.entry(0x130e, "Wyze Labs, Inc."), + Map.entry(0x130f, "Z-Wave Europe GmbH"), Map.entry(0x1310, "AEOTEC LIMITED"), + Map.entry(0x1311, "NGSTB Company Limited"), + Map.entry(0x1312, "Qingdao Yeelink Information Technology Co., Ltd."), + Map.entry(0x1313, "E-Smart Home Automation Systems Limited"), Map.entry(0x1314, "Fibar Group S.A."), + Map.entry(0x1315, "Prolitech GmbH"), Map.entry(0x1316, "PanKore Integrated Circuit Technology Co. Ltd."), + Map.entry(0x1317, "Logitech"), Map.entry(0x1318, "Piaro, Inc."), + Map.entry(0x1319, "Mitsubishi Electric US, Inc."), Map.entry(0x131a, "Resideo Technologies, Inc."), + Map.entry(0x131b, "Espressif Systems (Shanghai) Co., Ltd."), + Map.entry(0x131c, "HELLA Sonnen- und Wetterschutztechnik GmbH"), + Map.entry(0x131d, "Geberit International AG"), Map.entry(0x131e, "CAME S.p.A."), + Map.entry(0x131f, "Guangzhou Elite Education & Technology Co., Ltd."), + Map.entry(0x1320, "Phyplus Microelectronics Limited"), + Map.entry(0x1321, "Shenzhen Sonoff Technologies Co., Ltd."), Map.entry(0x1322, "Safe4 Security Group"), + Map.entry(0x1323, "Shanghai MXCHIP Information Technology Co., Ltd."), Map.entry(0x1324, "HDC I-Controls"), + Map.entry(0x1325, "Zuma Array Limited"), Map.entry(0x1326, "DECELECT"), + Map.entry(0x1327, "Mill International AS"), Map.entry(0x1328, "HomeWizard BV"), + Map.entry(0x1329, "Shenzhen Topband Co., Ltd"), Map.entry(0x132a, "Pressac Communications Ltd"), + Map.entry(0x132b, "Origin Wireless, Inc."), Map.entry(0x132c, "Connecte AS"), Map.entry(0x132d, "YOKIS"), + Map.entry(0x132e, "Xiamen Yankon Energetic Lighting Co., Ltd."), Map.entry(0x132f, "Yandex LLC"), + Map.entry(0x1330, "Critical Software S.A."), Map.entry(0x1331, "Nortek Control"), + Map.entry(0x1332, "BrightAI"), Map.entry(0x1333, "Becker-Antriebe GmbH"), + Map.entry(0x1334, "Shenzhen TCL New Technology Company Limited"), + Map.entry(0x1335, "Dexatek Technology Ltd"), Map.entry(0x1336, "Elelabs International Limited"), + Map.entry(0x1337, "Datek Wireless AS"), Map.entry(0x1338, "ALDES"), Map.entry(0x1339, "Savant Company"), + Map.entry(0x133a, "Ariston Thermo Group"), Map.entry(0x133b, "WAREMA Renkhoff SE"), + Map.entry(0x133c, "VTech Holdings Limited"), Map.entry(0x133d, "Futurehome AS"), + Map.entry(0x133e, "Cognitive Systems Corp."), + Map.entry(0x133f, "ASR Microelectronics (Shenzhen) Co., Ltd."), Map.entry(0x1340, "Airios"), + Map.entry(0x1341, "Guangdong OPPO Mobile Telecommunications Corp., Ltd."), + Map.entry(0x1342, "Beken Corporation"), Map.entry(0x1343, "Corsair"), Map.entry(0x1344, "Eltako GmbH"), + Map.entry(0x1345, "Chengdu Meross Technology Co., Ltd."), + Map.entry(0x1346, "Rafael Microelectronics, Inc."), Map.entry(0x1347, "Aug. Winkhuas GmbH & Co. KG"), + Map.entry(0x1348, "Qingdao Haier Technology Co., Ltd."), Map.entry(0x1349, "Apple Inc."), + Map.entry(0x134a, "Rollease Acmeda"), Map.entry(0x134b, "Nabu Casa, Inc."), + Map.entry(0x134c, "Simon Holding"), Map.entry(0x134d, "KD Navien"), Map.entry(0x134e, "tado GmbH"), + Map.entry(0x134f, "Mediola Connected Living AG"), Map.entry(0x1350, "Polynhome"), + Map.entry(0x1351, "HooRii Technology Co., Ltd."), Map.entry(0x1352, "Häfele SE & Co KG"), + Map.entry(0x1353, "KIMIN Electronics Co., Ltd."), Map.entry(0x1354, "Zyax AB"), + Map.entry(0x1355, "Baracoda SA"), Map.entry(0x1356, "Lennox International, Inc"), + Map.entry(0x1357, "Teledatics Incorporated"), Map.entry(0x1358, "Top Victory Investments Limited"), + Map.entry(0x1359, "GOQUAL Inc."), Map.entry(0x135a, "Siegenia Aubi KG"), + Map.entry(0x135b, "Virtual Connected Controlling System (Singapore) Pte. Ltd."), + Map.entry(0x135c, "Gigaset Communications GmbH"), Map.entry(0x135d, "Nuki Home Solutions GmbH"), + Map.entry(0x135e, "Devicebook, Inc."), Map.entry(0x135f, "Consumer 2.0 Inc. (Rently)"), + Map.entry(0x1360, "Edison Labs, Inc. (dba Orro)"), Map.entry(0x1361, "Inovelli"), + Map.entry(0x1362, "deveritec GmbH"), Map.entry(0x1363, "Charter Communications"), + Map.entry(0x1364, "Monolithic Power Systems, Inc."), + Map.entry(0x1365, "Ningbo Dooya Mechanic & Electronic Technology Co., Ltd"), + Map.entry(0x1366, "Shenzhen SDMC Technology Co., Ltd."), Map.entry(0x1367, "HP Inc."), + Map.entry(0x1368, "mui Lab, Inc."), Map.entry(0x1369, "BHtronics S.r.l."), + Map.entry(0x136a, "Akuvox (Xiamen) Networks Co., Ltd."), Map.entry(0x136b, "nami"), + Map.entry(0x136c, "Kee Tat Manufactory Holdings Limited"), Map.entry(0x136d, "Iton Technology Corp."), + Map.entry(0x136e, "Ambi Labs Limited"), Map.entry(0x136f, "Corporación Empresarial Altra S.L."), + Map.entry(0x1370, "Coway Co., Ltd."), Map.entry(0x1371, "Tridonic GmbH & Co KG"), + Map.entry(0x1372, "innovation matters iot GmbH"), Map.entry(0x1373, "MediaTek Inc."), + Map.entry(0x1374, "Fresco"), Map.entry(0x1375, "Hangzhou Yaguan Technology Co., Ltd."), + Map.entry(0x1376, "Guardian Glass, LLC"), Map.entry(0x1377, "Night Owl SP, LLC"), + Map.entry(0x1378, "Je Woo Corporation Ltd."), Map.entry(0x1379, "Earda Technologies Co., Ltd."), + Map.entry(0x137a, "Alexa Connect Kit (ACK)"), Map.entry(0x137b, "Amazon Basics"), + Map.entry(0x137c, "Morse Micro Inc."), Map.entry(0x137d, "Shanghai Xiaodu Technology Limited"), + Map.entry(0x137e, "Nubert electronic GmbH"), Map.entry(0x137f, "Shenzhen NEO Electronics Co. Ltd."), + Map.entry(0x1380, "Grimsholm Products AB"), Map.entry(0x1381, "Amazon Prime Video"), + Map.entry(0x1382, "ION INDUSTRIES B.V."), Map.entry(0x1383, "Ayla Networks"), + Map.entry(0x1384, "Apple Keychain"), Map.entry(0x1385, "Lightning Semiconductor"), + Map.entry(0x1386, "Skylux NV"), Map.entry(0x1387, "Shenzhen Qianyan Technology Ltd."), + Map.entry(0x1388, "Infineon Technologies AG"), Map.entry(0x1389, "Shenzhen Jingxun Technology Co., Ltd."), + Map.entry(0x138a, "Nature Inc."), Map.entry(0x138b, "WiFigarden Inc."), + Map.entry(0x138c, "Hisense Group Co. Ltd., USA"), + Map.entry(0x138d, "Nanjing Easthouse Electrical Co., Ltd."), Map.entry(0x138e, "Ledworks SRL"), + Map.entry(0x138f, "Shina System Co., Ltd."), Map.entry(0x1390, "Qualcomm Technologies Inc."), + Map.entry(0x1391, "Kasa (Big Field Global PTE. Ltd.)"), + Map.entry(0x1392, "Tapo (Big Field Global PTE. Ltd.)"), + Map.entry(0x1393, "Shanghai High-Flying Electronics Technology Co., Ltd."), + Map.entry(0x1394, "SigmaStar Technology Ltd."), Map.entry(0x1395, "HOOBS Inc."), + Map.entry(0x1396, "AiDot Inc."), Map.entry(0x1397, "Woan Technology (Shenzhen) Co., Ltd."), + Map.entry(0x1398, "Meizu Technology Co., Ltd."), Map.entry(0x1399, "Yukai Engineering Inc."), + Map.entry(0x139a, "Qrio, Inc."), Map.entry(0x139b, "ITIUS GmbH"), + Map.entry(0x139c, "Zemismart Technology Limited"), Map.entry(0x139d, "LED Linear GmbH"), + Map.entry(0x139e, "Dyson Technology Limited"), Map.entry(0x139f, "Razer Inc."), + Map.entry(0x1400, "Uascent Technology Company Limited"), Map.entry(0x1401, "Bose Corporation"), + Map.entry(0x1402, "GOLDTek Technology Co., Ltd."), Map.entry(0x1403, "Arlec Australia Pty. Ltd."), + Map.entry(0x1404, "Shenzhen Phaten Technology Co., Ltd."), Map.entry(0x1405, "Ecovacs Robotics Co., Ltd."), + Map.entry(0x1406, "Luxshare-ICT Co., Ltd."), Map.entry(0x1407, "Jiangshu Shushi Technology Co., Ltd."), + Map.entry(0x1408, "Velux A/S"), Map.entry(0x1409, "Shenzhen Hidin Technology Co., Ltd."), + Map.entry(0x140a, "Intertech Services AG"), Map.entry(0x140b, "70mai Co., Ltd."), + Map.entry(0x140c, "Beijing ESWIN Computing Technology CO.,Ltd."), + Map.entry(0x140d, "Photon Sail Technologies Pte. Ltd."), Map.entry(0x140e, "WiDom SRL"), + Map.entry(0x140f, "Sagemcom SAS"), Map.entry(0x1410, "Quectel Wireless Solutions Co., Ltd."), + Map.entry(0x1411, "Freedompro S.r.l."), Map.entry(0x1412, "Disign Incorporated"), + Map.entry(0x1413, "1Home Solutions GmbH"), Map.entry(0x1414, "StreamUnlimited Engineering GmbH"), + Map.entry(0x1415, "Caveman (Nanoleaf)"), Map.entry(0x1416, "Umbra (Nanoleaf)"), + Map.entry(0x1417, "Konnected Inc."), Map.entry(0x1418, "KLite (Signify)"), + Map.entry(0x1419, "Lorex Technology Inc."), Map.entry(0x141a, "RATOC Systems, Inc"), + Map.entry(0x141b, "Rang Dong Light Source & VacuumFlask Joint Stock Company"), + Map.entry(0x141c, "Shenzhen Sibo Zhilian Technology Co., Ltd."), Map.entry(0x141d, "Secuyou APS"), + Map.entry(0x141e, "TUO Accessories LLC"), Map.entry(0x141f, "DUCTECH Co., Ltd"), + Map.entry(0x1420, "EcoFlow Inc."), Map.entry(0x1421, "Kwikset"), + Map.entry(0x1422, "Zhuhai HiVi Technology Co., Ltd."), Map.entry(0x1423, "Feit Electric Company, Inc."), + Map.entry(0x1424, "Alarm.com Incorporated"), Map.entry(0x1425, "Hangzhou BroadLink Technology Co., Ltd."), + Map.entry(0x1426, "ELE (Group) Co., Ltd."), Map.entry(0x1427, "Hama GmbH & Co. KG"), + Map.entry(0x1428, "Shenzhen Aimore .Co .,Ltd"), Map.entry(0x1429, "Albrecht Jung GmbH & Co. KG"), + Map.entry(0x142a, "Hitachi Global Life Solutions, Inc."), + Map.entry(0x142b, "Beijing Renhejia Technology Co., Ltd"), + Map.entry(0x142c, "vivo Mobile Communication Co., Ltd."), + Map.entry(0x142d, "Zhongshan QIHANG Electronic Technology Co."), + Map.entry(0x142e, "Shenzhen Sowye Technology CO.,Ltd"), + Map.entry(0x142f, "Shenzhen QIACHIP Wireless Ecommerce Co."), Map.entry(0x1430, "L-TRADE GROUP SP z.o.o."), + Map.entry(0x1431, "Daikin Industries, Ltd."), Map.entry(0x1432, "ELKO EP, s.r.o."), + Map.entry(0x1433, "MOMAX Technology (Hong Kong) Limited"), + Map.entry(0x1434, "Hangzhou Ezviz Network Co., Ltd."), Map.entry(0x1435, "Granite River Labs"), + Map.entry(0x1436, "SinuxSoft Inc."), Map.entry(0x1437, "ACCEL LAB Ltd."), + Map.entry(0x1438, "Xiamen Topstar Lighting Co.,Ltd"), Map.entry(0x1439, "Vaillant Group"), + Map.entry(0x143a, "YoSmart Inc."), Map.entry(0x143b, "Amina Charging AS"), Map.entry(0x143c, "Athom B.V."), + Map.entry(0x143d, "Shenzhen Champon Technology Co., Ltd"), Map.entry(0x143e, "Acer Inc."), + Map.entry(0x143f, "Vestel Elektronik Sanayi ve Ticaret A.S."), Map.entry(0x1440, "VerLuce"), + Map.entry(0x1441, "Shenzhen Snowball Technology Co., Ltd."), Map.entry(0x1442, "REHAU Group"), + Map.entry(0x1443, "GoodsiQ"), Map.entry(0x1444, "Last lock Inc."), Map.entry(0x1445, "Finesse Decor"), + Map.entry(0x1446, "Take As Global, SL"), Map.entry(0x1447, "Honor Device Co., Ltd."), + Map.entry(0x1448, "LivingStyle Enterprises Limited"), Map.entry(0x1449, "ZUTTO TECHNOLOGIES"), + Map.entry(0x144a, "Sensibo Ltd."), Map.entry(0x144b, "Kohler Company"), + Map.entry(0x144c, "TrustAsia Technologies, Inc."), Map.entry(0x144d, "Atios AG"), + Map.entry(0x144e, "Sense Labs, Inc."), Map.entry(0x144f, "Assa Abloy AB"), + Map.entry(0x1450, "GM Global Technology Operations LLC"), Map.entry(0x1451, "JetHome"), + Map.entry(0x1452, "Big Ass Fans"), Map.entry(0x1453, "Gumax BV"), Map.entry(0x1454, "Yardi Systems Inc."), + Map.entry(0x1455, "Deutsche Telekom AG"), Map.entry(0x1456, "Sensirion AG"), + Map.entry(0x1457, "Hangzhou Wistar Mechanical & Electric Technology Co., Ltd"), + Map.entry(0x1458, "Wilhelm Koch GmbH "), Map.entry(0x1459, "Shenzhen iComm Semiconductor Co., Ltd."), + Map.entry(0x145a, "British Telecommunications plc"), Map.entry(0x145b, "Remotec Technology Ltd."), + Map.entry(0x145c, "Pin Genie, Inc. DBA Lockly"), Map.entry(0x145d, "Hosiden Corporation"), + Map.entry(0x145e, "Deako, Inc."), Map.entry(0x145f, "Good Way Technology Co., Ltd."), + Map.entry(0x1460, "Zhuhai Ruran Intelligent Technology Co., LTD (Meizu)"), + Map.entry(0x1461, "Xinda Asset Management (Shenzhen) Co.,Ltd."), + Map.entry(0x1462, "Chengdu Energy Magic Cube Technology Co., Ltd"), + Map.entry(0x1463, "Eberle Controls GmbH"), Map.entry(0x1464, "Opulinks Technology"), + Map.entry(0x1465, "Hunter Douglas Group"), Map.entry(0x1466, "Hangzhou Hemos Lighting Company Limited"), + Map.entry(0x1467, "OTODO SAS"), Map.entry(0x1468, "Anona Security Technology Limited"), + Map.entry(0x1469, "Loxone Electronics GmbH"), Map.entry(0x146a, "Intecular LLC"), + Map.entry(0x146b, "Aixlink Ltd."), Map.entry(0x146c, "Shenzhen Jinjie Technology Co.,Ltd."), + Map.entry(0x146d, "Polyaire Pty Ltd"), Map.entry(0x146e, "Shenzhen PINXUAN Trading Co."), + Map.entry(0x146f, "SmartWing Home LLC"), Map.entry(0x1470, "Shenzhen Hope Microelectronics Co., Ltd."), + Map.entry(0x1471, "Commax"), Map.entry(0x1472, "Zhejiang Jiecang Linear Motion Technology Co.,Ltd"), + Map.entry(0x1473, "Shenzhen Lelight technology Co.lt"), + Map.entry(0x1474, "Shenzhen Ruomu Zhilian Technology Co., Ltd."), + Map.entry(0x1475, "Cable Television Laboratories, Inc. dba CableLabs"), + Map.entry(0x1476, "Harman International"), Map.entry(0x1477, "Shenzhen Multi IR Technology Co.,Ltd"), + Map.entry(0x1478, "APYNOV"), Map.entry(0x1479, "Browan Communications Inc."), + Map.entry(0x147a, "Shenzhen Realwe Innovation Technology Co., Ltd."), Map.entry(0x147b, "Lumiflow INC"), + Map.entry(0x147c, "SHENZHEN SHENAN YANGGUANG ELECTRONICS CO., LTD."), + Map.entry(0x147d, "Wenzhou Morning Electronics Co., Ltd."), Map.entry(0x147e, "MIWA Lock Co., Ltd."), + Map.entry(0x147f, "U-tec Group Inc."), Map.entry(0x1480, "Beijing Roborock Technology Co., Ltd."), + Map.entry(0x1481, "Shenzhen Xenon Industrial Ltd"), + Map.entry(0x1482, "Guangzhou Lingqu Electronic Technology Co., Ltd"), + Map.entry(0x1483, "Shenzhen Jijia Intelligent Technology Co., Ltd."), + Map.entry(0x1484, "CANDY HOUSE, Inc."), Map.entry(0x1485, "ELIT Scandinavia ApS"), + Map.entry(0x1486, "Infibrite Inc"), Map.entry(0x1487, "Whirlpool Corp."), + Map.entry(0x1488, "Shortcut Labs (Flic)"), Map.entry(0x1489, "INTEREL BUILDING AUTOMATION"), + Map.entry(0x148a, "Occhio GmbH"), Map.entry(0x148b, "Samraj Technologies Limited"), + Map.entry(0x1994, "Gewiss S.p.A."), Map.entry(0x2794, "Climax Technology Co., Ltd."), + Map.entry(0x6006, "Google LLC"), Map.entry(0xc5a0, "Connectivity Standards Alliance"), + Map.entry(0xc5a1, "Connectivity Standards Alliance"), Map.entry(0xc5a2, "Connectivity Standards Alliance"), + Map.entry(0xc5a3, "Connectivity Standards Alliance"), Map.entry(0xc5a4, "Connectivity Standards Alliance"), + Map.entry(0xc5a5, "Connectivity Standards Alliance"), Map.entry(0xc5a6, "Connectivity Standards Alliance"), + Map.entry(0xc5a7, "Connectivity Standards Alliance"), Map.entry(0xc5a8, "Connectivity Standards Alliance"), + Map.entry(0xc5a9, "Connectivity Standards Alliance"), Map.entry(0xc5aa, "Connectivity Standards Alliance"), + Map.entry(0xc5ab, "Connectivity Standards Alliance"), Map.entry(0xc5ac, "Connectivity Standards Alliance"), + Map.entry(0xc5ad, "Connectivity Standards Alliance"), Map.entry(0xc5ae, "Connectivity Standards Alliance"), + Map.entry(0xc5af, "Connectivity Standards Alliance"), Map.entry(0xfff1, "[Test vendor #1]"), + Map.entry(0xfff2, "[Test vendor #2]"), Map.entry(0xfff3, "[Test vendor #3]"), + Map.entry(0xfff4, "[Test vendor #4]"), Map.entry(0xfff5, "[Reserved]"), Map.entry(0xfff6, "[Reserved]"), + Map.entry(0xfff7, "[Reserved]"), Map.entry(0xfff8, "[Reserved]"), Map.entry(0xfff9, "[Reserved]"), + Map.entry(0xfffa, "[Reserved]"), Map.entry(0xfffb, "[Reserved]"), Map.entry(0xfffc, "[Reserved]"), + Map.entry(0xfffd, "[Reserved]"), Map.entry(0xfffe, "[Reserved]"), Map.entry(0xffff, "[Reserved]")); +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/ThreadDataset.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/ThreadDataset.java new file mode 100644 index 00000000000..e942d3b4429 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/ThreadDataset.java @@ -0,0 +1,1141 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.HashSet; +import java.util.HexFormat; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeSet; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +/** + * This class represents a Thread dataset. + * It supports both JSON and hex serialization according to the Thread specification. + * The JSON format is compatible with the OTBR JSON format. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ThreadDataset { + + private static final long DEFAULT_CHANNEL_MASK = 0x07FFF800L; // Decimal: 134152192, 2.4 GHz channels 11-26 + + private static final int TLV_CHANNEL = 0; + private static final int TLV_PAN_ID = 1; + private static final int TLV_EXT_PAN_ID = 2; + private static final int TLV_NETWORK_NAME = 3; + private static final int TLV_PSKC = 4; + private static final int TLV_NETWORK_KEY = 5; + private static final int TLV_MESH_LOCAL_PREFIX = 7; + private static final int TLV_SECURITY_POLICY = 12; + private static final int TLV_ACTIVE_TIMESTAMP = 14; + private static final int TLV_PENDING_TIMESTAMP = 51; + private static final int TLV_DELAY_TIMER = 52; + private static final int TLV_CHANNEL_MASK = 53; + + private static final String TYPE_ACTIVE_TIMESTAMP = "ActiveTimestamp"; + private static final String TYPE_PENDING_TIMESTAMP = "PendingTimestamp"; + private static final String TYPE_DELAY_TIMER = "DelayTimer"; + private static final String TYPE_CHANNEL = "Channel"; + private static final String TYPE_CHANNEL_MASK = "ChannelMask"; + private static final String TYPE_PAN_ID = "PanId"; + private static final String TYPE_NETWORK_NAME = "NetworkName"; + private static final String TYPE_NETWORK_KEY = "NetworkKey"; + private static final String TYPE_EXT_PAN_ID = "ExtPanId"; + private static final String TYPE_MESH_LOCAL_PREFIX = "MeshLocalPrefix"; + private static final String TYPE_PSKC = "PSKc"; + private static final String TYPE_SECURITY_POLICY = "SecurityPolicy"; + + private static final String PROP_SECONDS = "Seconds"; + private static final String PROP_TICKS = "Ticks"; + private static final String PROP_AUTHORITATIVE = "Authoritative"; + private static final String PROP_ROTATION_TIME = "RotationTime"; + private static final String PROP_OBTAIN_NETWORK_KEY = "ObtainNetworkKey"; + private static final String PROP_NATIVE_COMMISSIONING = "NativeCommissioning"; + private static final String PROP_ROUTERS = "Routers"; + private static final String PROP_EXTERNAL_COMMISSIONING = "ExternalCommissioning"; + private static final String PROP_COMMERCIAL_COMMISSIONING = "CommercialCommissioning"; + private static final String PROP_AUTONOMOUS_ENROLLMENT = "AutonomousEnrollment"; + private static final String PROP_NETWORK_KEY_PROVISIONING = "NetworkKeyProvisioning"; + private static final String PROP_TOBLE_LINK = "TobleLink"; + private static final String PROP_NON_CCM_ROUTERS = "NonCcmRouters"; + + /* Security Policy Flags */ + private static final int SP_OBTAIN_NETWORK_KEY = 1 << 15; + private static final int SP_NATIVE_COMMISSIONING = 1 << 14; + private static final int SP_ROUTERS = 1 << 13; + private static final int SP_EXTERNAL_COMM = 1 << 12; + private static final int SP_COMMERCIAL_COMM = 1 << 10; + private static final int SP_AUTO_ENROLL = 1 << 9; + private static final int SP_NET_KEY_PROV = 1 << 8; + private static final int SP_TO_BLE_LINK = 1 << 7; + private static final int SP_NON_CCM_ROUTERS = 1 << 6; + private static final int SP_RSV = 0x0038; + + private static final int MK_KEY_LEN = 16; + + private static final int PBKDF_ITERATIONS = 16_384; + private static final int PBKDF_KEY_LENGTH = 128; + + private static final long MIN_DELAY_MS = 30_000L; + private static final long MAX_DELAY_MS = 259_200_000L; + + private static final String PBKDF_ALG = "PBKDF2WithHmacSHA256"; + + private static final String DEFAULT_SECURITY_POLICY = "02A0F3B8"; + + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(ThreadDataset.class, new ThreadDatasetAdapter()).setPrettyPrinting().create(); + + private final TlvCodec codec = new TlvCodec(); + + public ThreadDataset() { + setSecurityPolicy(DEFAULT_SECURITY_POLICY); + setChannelMask(DEFAULT_CHANNEL_MASK); + } + + /** + * Create a new Thread dataset with the given parameters. + * TLV values are optional and may be null. + * + * @param activeTs + * @param pendingTs + * @param delayMs + * @param channel + * @param channelMask + * @param panId + * @param networkName + * @param networkKeyHex + * @param extPanIdHex + * @param pskcHex + * @param meshPrefix + * @param secPolicyHex + */ + public ThreadDataset(@Nullable ThreadTimestamp activeTs, @Nullable ThreadTimestamp pendingTs, + @Nullable Long delayMs, @Nullable Integer channel, @Nullable Long channelMask, @Nullable Integer panId, + @Nullable String networkName, @Nullable String networkKeyHex, @Nullable String extPanIdHex, + @Nullable String pskcHex, @Nullable String meshPrefix, @Nullable String secPolicyHex) { + if (activeTs != null) { + setActiveTimestamp(activeTs); + } + if (pendingTs != null) { + setPendingTimestamp(pendingTs); + } + if (delayMs != null) { + setDelayTimer(delayMs.longValue()); + } + if (channel != null) { + setChannel(channel.intValue()); + } + if (channelMask != null) { + setChannelMask(channelMask.longValue()); + } else { + setChannelMask(DEFAULT_CHANNEL_MASK); + } + if (panId != null) { + setPanId(panId.intValue()); + } + if (networkName != null) { + setNetworkName(networkName); + } + if (present(networkKeyHex)) { + setNetworkKey(Objects.requireNonNull(networkKeyHex)); + } + if (present(extPanIdHex)) { + setExtPanId(Objects.requireNonNull(extPanIdHex)); + } + if (present(pskcHex)) { + setPskc(Objects.requireNonNull(pskcHex)); + } + if (present(meshPrefix)) { + try { + setMeshLocalPrefix(parsePrefix(Objects.requireNonNull(meshPrefix))); + } catch (IOException e) { + throw new IllegalArgumentException("Bad mesh-local prefix: " + meshPrefix, e); + } + } + + if (present(secPolicyHex)) { + setSecurityPolicy(Objects.requireNonNull(secPolicyHex)); + } else { + setSecurityPolicy(DEFAULT_SECURITY_POLICY); + } + } + + public String toJson() { + return GSON.toJson(this); + } + + public Optional getActiveTimestamp() { + return codec.getUint64(TLV_ACTIVE_TIMESTAMP); + } + + public Optional getActiveTimestampHex() { + return toHex(getActiveTimestamp(), 16); + } + + public Optional getActiveTimestampObject() { + return getActiveTimestamp().map(ThreadTimestamp::fromLong); + } + + public Optional getPendingTimestamp() { + return codec.getUint64(TLV_PENDING_TIMESTAMP); + } + + public Optional getPendingTimestampHex() { + return toHex(getPendingTimestamp(), 16); + } + + public Optional getDelayTimer() { + return codec.getUint32(TLV_DELAY_TIMER); + } + + public Optional getDelayTimerHex() { + return toHex(getDelayTimer(), 8); + } + + public Optional getChannel() { + return codec.getBytes(TLV_CHANNEL).flatMap(b -> { + if (b.length != 3) { // Thread 1.4 requires a 3-byte Channel TLV + return Optional.empty(); + } + int msb = Byte.toUnsignedInt(b[1]); + int lsb = Byte.toUnsignedInt(b[2]); + return Optional.of((msb << 8) | lsb); + }); + } + + public Optional getChannelHex() { + return toHex(getChannel().map(Long::valueOf), 2); + } + + /** + * Get the channel mask as a long. + * Value the raw bits into their IEEE channel positions + * + * @return + */ + public Optional getChannelMask() { + return getChannelSet().map(set -> { + long mask = 0; + for (int ch : set) { + if (ch >= 0 && ch < 64) { + mask |= 1L << ch; // set bit at the channel number + } + } + return mask; + }); + } + + public Optional getChannelMaskHex() { + return toHex(getChannelMask(), 8); + } + + public Optional getPanId() { + return codec.getUint16(TLV_PAN_ID); + } + + public Optional getExtPanId() { + return codec.getBytes(TLV_EXT_PAN_ID); + } + + public Optional getExtPanIdHex() { + return getExtPanId().map(TlvCodec::bytesToHex); + } + + public Optional getNetworkName() { + return codec.getBytes(TLV_NETWORK_NAME).map(String::new); + } + + public Optional getNetworkKey() { + return codec.getBytes(TLV_NETWORK_KEY); + } + + public Optional getNetworkKeyHex() { + return getNetworkKey().map(TlvCodec::bytesToHex); + } + + public Optional getPskc() { + return codec.getBytes(TLV_PSKC); + } + + public Optional getPskcHex() { + return getPskc().map(TlvCodec::bytesToHex); + } + + public Optional getMeshLocalPrefix() { + return codec.getBytes(TLV_MESH_LOCAL_PREFIX); + } + + public Optional getMeshLocalPrefixHex() { + return getMeshLocalPrefix().map(TlvCodec::bytesToHex); + } + + public Optional getMeshLocalPrefixFormatted() { + return getMeshLocalPrefix().map(pfx -> { + String hex = TlvCodec.bytesToHex(pfx).toLowerCase(Locale.ROOT); + return hex.substring(0, 4) + ":" + hex.substring(4, 8) + ":" + hex.substring(8, 12) + ":" + + hex.substring(12, 16) + "::/64"; + }); + } + + public Optional getSecurityPolicyRotation() { + return codec.getBytes(TLV_SECURITY_POLICY).map(b -> ByteBuffer.wrap(b).getShort(0) & 0xFFFF); + } + + public Optional getSecurityPolicyFlags() { + return codec.getBytes(TLV_SECURITY_POLICY).map(b -> { + if (b.length >= 4) { + return (Byte.toUnsignedInt(b[2]) << 8) | Byte.toUnsignedInt(b[3]); + } else { + return Byte.toUnsignedInt(b[2]) << 8; + } + }); + } + + public Optional getSecurityPolicyHex() { + return codec.getBytes(TLV_SECURITY_POLICY).map(TlvCodec::bytesToHex); + } + + public void setActiveTimestamp(long ts) { + codec.putUint64(TLV_ACTIVE_TIMESTAMP, ts); + } + + public void setActiveTimestamp(ThreadTimestamp ts) { + setActiveTimestamp(ts.toLong()); + } + + public void setActiveTimestampAuthoritative(boolean authoritative) { + long ts = Objects.requireNonNull(getActiveTimestamp().orElse(0L)); + if (authoritative) { + ts |= 0x1L; // set bit 0 + } else { + ts &= ~0x1L; // unset bit 0 + } + setActiveTimestamp(ts); + } + + public void setActiveTimestamp(String h) { + setActiveTimestamp(Long.parseLong(TlvCodec.strip0x(h), 16)); + } + + public void setPendingTimestamp(long ts) { + codec.putUint64(TLV_PENDING_TIMESTAMP, ts); + } + + public void setPendingTimestamp(ThreadTimestamp ts) { + setPendingTimestamp(ts.toLong()); + } + + public void setPendingTimestamp(String h) { + setPendingTimestamp(Long.parseLong(TlvCodec.strip0x(h), 16)); + } + + public void setDelayTimer(long ms) { + if (ms < MIN_DELAY_MS || ms > MAX_DELAY_MS) { + throw new IllegalArgumentException( + "DelayTimer must be between 30000ms (30 seconds) and 259200000ms (72 hours)"); + } + codec.putUint32(TLV_DELAY_TIMER, ms); + } + + public void setDelayTimer(String h) { + setDelayTimer(Long.parseLong(TlvCodec.strip0x(h), 16)); + } + + public void setChannel(int ch) { + ByteBuffer bb = ByteBuffer.allocate(3); + bb.put((byte) 0); // page 0 (2.4 GHz) + bb.putShort((short) ch); + codec.putBytes(TLV_CHANNEL, bb.array()); + } + + public void setChannelMask(long rawMask) { + Set channels = new TreeSet<>(); + for (int i = 0; i < 64; i++) { + if (((rawMask >> i) & 1) != 0) { + channels.add(i); + } + } + setChannelSet(channels); + } + + public void setPanId(int pan) { + codec.putUint16(TLV_PAN_ID, pan); + } + + public void setExtPanId(byte[] id) { + codec.putBytes(TLV_EXT_PAN_ID, id, 8); + } + + public void setExtPanId(String hex) { + setExtPanId(TlvCodec.hexStringToBytes(fixLength(TlvCodec.strip0x(hex), 16))); + } + + public void setNetworkName(String name) { + codec.putBytes(TLV_NETWORK_NAME, name.getBytes()); + } + + public void setNetworkKey(byte[] key) { + codec.putBytes(TLV_NETWORK_KEY, key, 16); + } + + public void setNetworkKey(String hex) { + setNetworkKey(TlvCodec.hexStringToBytes(fixLength(TlvCodec.strip0x(hex), 32))); + } + + public void setPskc(byte[] key) { + codec.putBytes(TLV_PSKC, key, 16); + } + + public void setPskc(String hex) { + setPskc(TlvCodec.hexStringToBytes(fixLength(TlvCodec.strip0x(hex), 32))); + } + + public void setMeshLocalPrefix(byte[] pfx) { + codec.putBytes(TLV_MESH_LOCAL_PREFIX, pfx, 8); + } + + public void setMeshLocalPrefix(String hex) { + setMeshLocalPrefix(TlvCodec.hexStringToBytes(fixLength(TlvCodec.strip0x(hex), 16))); + } + + public void setMeshLocalPrefixFormatted(String formatted) { + try { + setMeshLocalPrefix(parsePrefix(formatted)); + } catch (IOException e) { + throw new IllegalArgumentException("Invalid mesh local prefix: " + formatted, e); + } + } + + public void setSecurityPolicy(int rotationHours, int flags) { + if (rotationHours < 2) { + throw new IllegalArgumentException("RotationTime must be greater than 2 hours"); + } + // Reserved bits 5‑3 MUST be 1 + int f = flags | SP_RSV; + ByteBuffer bb = ByteBuffer.allocate(4); + bb.putShort((short) rotationHours); + bb.put((byte) (f >> 8)); // high‑order flag byte + bb.put((byte) f); // low‑order byte (Rsv+VR) + codec.putBytes(TLV_SECURITY_POLICY, bb.array()); + } + + public void setSecurityPolicy(String hex) { + codec.putBytes(TLV_SECURITY_POLICY, TlvCodec.hexStringToBytes(TlvCodec.strip0x(hex))); + } + + public boolean isObtainNetworkKey() { + return hasFlag(SP_OBTAIN_NETWORK_KEY); + } + + public void setObtainNetworkKey(boolean on) { + setFlag(SP_OBTAIN_NETWORK_KEY, on); + } + + public boolean isNativeCommissioning() { + return hasFlag(SP_NATIVE_COMMISSIONING); + } + + public void setNativeCommissioning(boolean on) { + setFlag(SP_NATIVE_COMMISSIONING, on); + } + + public boolean isRoutersEnabled() { + return hasFlag(SP_ROUTERS); + } + + public void setRoutersEnabled(boolean on) { + setFlag(SP_ROUTERS, on); + } + + public boolean isCommercialCommissioning() { + return hasFlag(SP_COMMERCIAL_COMM); + } + + public void setCommercialCommissioning(boolean on) { + setFlag(SP_COMMERCIAL_COMM, on); + } + + public boolean isExternalCommissioning() { + return hasFlag(SP_EXTERNAL_COMM); + } + + public void setExternalCommissioning(boolean on) { + setFlag(SP_EXTERNAL_COMM, on); + } + + public boolean isAutonomousEnrollment() { + return hasFlag(SP_AUTO_ENROLL); + } + + public void setAutonomousEnrollment(boolean on) { + setFlag(SP_AUTO_ENROLL, on); + } + + public boolean isNetworkKeyProvisioning() { + return hasFlag(SP_NET_KEY_PROV); + } + + public void setNetworkKeyProvisioning(boolean on) { + setFlag(SP_NET_KEY_PROV, on); + } + + public boolean isToBleLink() { + return hasFlag(SP_TO_BLE_LINK); + } + + public void setToBleLink(boolean on) { + setFlag(SP_TO_BLE_LINK, on); + } + + public boolean isNonCcmRouters() { + return hasFlag(SP_NON_CCM_ROUTERS); + } + + public void setNonCcmRouters(boolean on) { + setFlag(SP_NON_CCM_ROUTERS, on); + } + + public Optional> getChannelSet() { + return codec.getBytes(TLV_CHANNEL_MASK).map(b -> { + // TLV must be at least 3 bytes: page, maskLen, at least 1 mask byte + if (b.length < 3) { + return Set.of(); + } + int page = Byte.toUnsignedInt(b[0]); + int maskLen = Byte.toUnsignedInt(b[1]); + // Must be exactly 2 + maskLen bytes, and maskLen 1..4 + if (maskLen < 1 || maskLen > 4 || b.length != 2 + maskLen) { + return Set.of(); + } + int baseChannel = pageToBaseChannel(page); + int mask = 0; + for (int i = 0; i < maskLen; i++) { + mask |= (Byte.toUnsignedInt(b[2 + i]) << (8 * (maskLen - 1 - i))); + } + Set channels = new TreeSet<>(); + for (int i = 0; i < 32; i++) { + if (((mask >> i) & 1) != 0) { + channels.add(baseChannel + i); + } + } + return channels; + }); + } + + /** + * Set the channel mask using a set of channel numbers. + * The mask will be 4 bytes long, with the appropriate page and base channel. + * Channels outside the 32-channel window for the page are ignored. + */ + public void setChannelSet(Set channels) { + if (channels.isEmpty()) { + return; + } + int min = channels.stream().mapToInt(Integer::intValue).min().getAsInt(); + int page = 0; + int baseChannel = 11; + if (min >= 33) { + page = 2; + baseChannel = 33; + } + if (min >= 45) { + page = 4; + baseChannel = 45; + } + if (min >= 51) { + page = 5; + baseChannel = 51; + } + if (min >= 63) { + page = 6; + baseChannel = 63; + } + + int mask = 0; + for (int ch : channels) { + int bit = ch - baseChannel; + if (bit >= 0 && bit < 32) { + mask |= (1 << bit); + } + } + + ByteBuffer bb = ByteBuffer.allocate(6); + bb.put((byte) page); + bb.put((byte) 0x04); // mask length + bb.putInt(mask); + codec.putBytes(TLV_CHANNEL_MASK, bb.array()); + } + + /** + * Convert the dataset to a hex string. + * The hex string preserves the canonical order for the primary TLVs. + * + * @return the hex string + */ + public String toHex() { + // canonical order for the 10 TLVs that appear in every Active dataset, matches the order a OTBR uses + int[] canonical = { TLV_ACTIVE_TIMESTAMP, TLV_CHANNEL, TLV_CHANNEL_MASK, TLV_EXT_PAN_ID, TLV_MESH_LOCAL_PREFIX, + TLV_NETWORK_KEY, TLV_NETWORK_NAME, TLV_PAN_ID, TLV_PSKC, TLV_SECURITY_POLICY }; + Set canonicalSet = new HashSet<>(); + for (int t : canonical) { + canonicalSet.add(t); + } + + ByteBuffer out = ByteBuffer.allocate(1024); + + // loop through canonical TLVs first + for (int type : canonical) { + byte[] val = codec.getBytes(type).orElse(null); + if (val != null) { + if (val.length > 255) { + throw new IllegalArgumentException("TLV >255 B"); + } + out.put((byte) type).put((byte) val.length).put(val); + } + } + + // loop through all others once, in ascending order + codec.asMap().entrySet().stream().filter(e -> !canonicalSet.contains(e.getKey())) + .sorted(Map.Entry.comparingByKey()).forEach(e -> { + byte[] val = e.getValue(); + if (val.length > 255) { + throw new IllegalArgumentException("TLV >255 B"); + } + out.put(e.getKey().byteValue()).put((byte) val.length).put(val); + }); + + return TlvCodec.bytesToHex(Arrays.copyOf(out.array(), out.position())).toUpperCase(); + } + + public static ThreadDataset fromHex(String hex) { + Objects.requireNonNull(hex, "hex string"); + hex = hex.replaceAll("\\s+", ""); + if (hex.length() % 2 != 0) { + throw new IllegalArgumentException("Odd-length hex"); + } + byte[] buf = TlvCodec.hexStringToBytes(hex); + ThreadDataset ds = new ThreadDataset(); + for (int i = 0; i < buf.length;) { + int type = Byte.toUnsignedInt(buf[i++]); + int len = Byte.toUnsignedInt(buf[i++]); + if (i + len > buf.length) { + throw new IllegalArgumentException("TLV length exceeds buffer at type 0x" + Integer.toHexString(type)); + } + ds.codec.putBytes(type, Arrays.copyOfRange(buf, i, i + len)); + i += len; + } + return ds; + } + + public static @Nullable ThreadDataset fromJson(String json) { + return GSON.fromJson(json, ThreadDataset.class); + } + + /** + * Generate a new secure master key. + * + * @return the master key + */ + public static byte[] generateMasterKey() throws NoSuchAlgorithmException { + byte[] key = new byte[MK_KEY_LEN]; + SecureRandom.getInstanceStrong().nextBytes(key); + return key; + } + + public static byte[] generatePskc(String passPhrase, String networkName, String extPanIdHex) + throws GeneralSecurityException { + if (passPhrase.length() < 6 || passPhrase.length() > 255) { + throw new IllegalArgumentException( + "Pass-phrase must be 6–255 characters: current length is " + passPhrase.length()); + } + if (networkName.isEmpty() || networkName.length() > 16) { + throw new IllegalArgumentException( + "Network name must be 1–16 characters: current length is " + networkName.length()); + } + if (!extPanIdHex.matches("[0-9a-fA-F]{16}")) { + throw new IllegalArgumentException( + "ExtPANID must be 16 hex chars: current length is " + extPanIdHex.length()); + } + byte[] saltPrefix = "Thread".getBytes(StandardCharsets.US_ASCII); + byte[] nameUpper = networkName.toUpperCase().getBytes(StandardCharsets.US_ASCII); + byte[] extPanId = HexFormat.of().parseHex(extPanIdHex); + byte[] salt = new byte[saltPrefix.length + nameUpper.length + extPanId.length]; + System.arraycopy(saltPrefix, 0, salt, 0, saltPrefix.length); + System.arraycopy(nameUpper, 0, salt, saltPrefix.length, nameUpper.length); + System.arraycopy(extPanId, 0, salt, saltPrefix.length + nameUpper.length, extPanId.length); + PBEKeySpec spec = new PBEKeySpec(passPhrase.toCharArray(), salt, PBKDF_ITERATIONS, PBKDF_KEY_LENGTH); + SecretKeyFactory kf = SecretKeyFactory.getInstance(PBKDF_ALG); + return kf.generateSecret(spec).getEncoded(); + } + + private int currentFlags() { + return Objects.requireNonNull(getSecurityPolicyFlags().orElse(0)); + } + + private int currentRotation() { + return Objects.requireNonNull(getSecurityPolicyRotation().orElse(672)); + } + + public void setSecurityPolicyRotation(int hours) { + if (hours < 2) { + throw new IllegalArgumentException("RotationTime must be greater than 2 hours"); + } + setSecurityPolicy(hours, currentFlags()); + } + + private void setFlag(int bit, boolean on) { + int flags = currentFlags(); + flags = on ? (flags | bit) : (flags & ~bit); + setSecurityPolicy(currentRotation(), flags); + } + + private boolean hasFlag(int bit) { + return (currentFlags() & bit) != 0; + } + + private static boolean present(@Nullable String s) { + return s != null && !s.isBlank(); + } + + private static Optional toHex(Optional v, int width) { + return v.map(x -> String.format("%0" + width + "X", x.longValue())); + } + + private static String fixLength(String h, int chars) { + if (h.length() != chars) { + throw new IllegalArgumentException("Expected " + chars + " hex chars"); + } + return h; + } + + private static int pageToBaseChannel(int page) { + return switch (page) { + case 0 -> 11; + case 2 -> 33; + case 4 -> 45; + case 5 -> 51; + case 6 -> 63; + default -> 0; + }; + } + + private static byte[] parsePrefix(String text) throws IOException { + String ip = text.split("/")[0]; + try { + byte[] full = InetAddress.getByName(ip).getAddress(); + return Arrays.copyOf(full, 8); + } catch (UnknownHostException e) { + throw new IOException("Bad mesh-local prefix: " + text, e); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + codec.asMap().forEach((type, value) -> { + String hex = TlvCodec.bytesToHex(value); + String typeName = switch (type) { + case TLV_ACTIVE_TIMESTAMP -> TYPE_ACTIVE_TIMESTAMP; + case TLV_PENDING_TIMESTAMP -> TYPE_PENDING_TIMESTAMP; + case TLV_DELAY_TIMER -> TYPE_DELAY_TIMER; + case TLV_CHANNEL -> TYPE_CHANNEL; + case TLV_CHANNEL_MASK -> TYPE_CHANNEL_MASK; + case TLV_PAN_ID -> TYPE_PAN_ID; + case TLV_NETWORK_NAME -> TYPE_NETWORK_NAME; + case TLV_NETWORK_KEY -> TYPE_NETWORK_KEY; + case TLV_EXT_PAN_ID -> TYPE_EXT_PAN_ID; + case TLV_MESH_LOCAL_PREFIX -> TYPE_MESH_LOCAL_PREFIX; + case TLV_PSKC -> TYPE_PSKC; + case TLV_SECURITY_POLICY -> TYPE_SECURITY_POLICY; + default -> String.format("UnknownType(0x%02X)", type); + }; + sb.append(String.format(" %-18s = %s\n", typeName, hex)); + if (type == TLV_SECURITY_POLICY) { + sb.append(String.format(" %s = %d\n", PROP_ROTATION_TIME, getSecurityPolicyRotation().orElse(672))); + sb.append(String.format(" %s = %s\n", PROP_OBTAIN_NETWORK_KEY, isObtainNetworkKey())); + sb.append(String.format(" %s = %s\n", PROP_NATIVE_COMMISSIONING, isNativeCommissioning())); + sb.append(String.format(" %s = %s\n", PROP_ROUTERS, isRoutersEnabled())); + sb.append(String.format(" %s = %s\n", PROP_EXTERNAL_COMMISSIONING, isExternalCommissioning())); + sb.append(String.format(" %s = %s\n", PROP_COMMERCIAL_COMMISSIONING, isCommercialCommissioning())); + sb.append(String.format(" %s = %s\n", PROP_AUTONOMOUS_ENROLLMENT, isAutonomousEnrollment())); + sb.append(String.format(" %s = %s\n", PROP_NETWORK_KEY_PROVISIONING, isNetworkKeyProvisioning())); + sb.append(String.format(" %s = %s\n", PROP_TOBLE_LINK, isToBleLink())); + sb.append(String.format(" %s = %s\n", PROP_NON_CCM_ROUTERS, isNonCcmRouters())); + } + }); + return sb.toString(); + } + + /** + * Representation of a Thread timestamp. + *

+ * The 64‑bit packed format is laid out as: + * + *

+     * 63‥16 : seconds (48 bits)
+     * 15‥1  : ticks   (15 bits, 1/32768 s)
+     * 0     : authoritative flag
+     * 
+ * + * @author Dan Cunningham - Initial contribution + */ + public static class ThreadTimestamp { + + private long seconds; + private int ticks; + private boolean authoritative; + + /** + * Build a timestamp from the individual components. + * + * @param seconds + * @param ticks + * @param authoritative + */ + public ThreadTimestamp(long seconds, int ticks, boolean authoritative) { + if (seconds < 0 || seconds > 0x0000_FFFF_FFFF_FFFFL) { + throw new IllegalArgumentException("Invalid seconds value: " + seconds); + } + if (ticks < 0 || ticks > 0x7FFF) { + throw new IllegalArgumentException("Invalid ticks value: " + ticks); + } + setSeconds(seconds); + setTicks(ticks); + this.authoritative = authoritative; + } + + /** + * Factory for new timestamps based on the current time. + * + * @param authoritative + * @return + */ + public static ThreadTimestamp now(boolean authoritative) { + long millis = System.currentTimeMillis(); + long secs = (millis / 1000L) & 0x0000_FFFF_FFFF_FFFFL; + int t = (int) (((millis % 1000L) * 32_768L) / 1000L) & 0x7FFF; + return new ThreadTimestamp(secs, t, authoritative); + } + + /** + * Decode the timestamp from a long. + * + * @param value + * @return + */ + public static ThreadTimestamp fromLong(long value) { + long secs = (value >>> 16) & 0x0000_FFFF_FFFF_FFFFL; + int t = (int) ((value >>> 1) & 0x7FFF); + boolean auth = (value & 0x1L) != 0; + return new ThreadTimestamp(secs, t, auth); + } + + /** + * Encode a timestamp to a long. + * + * @return + */ + public long toLong() { + long v = (seconds & 0x0000_FFFF_FFFF_FFFFL) << 16; + v |= ((long) (ticks & 0x7FFF)) << 1; + if (authoritative) { + v |= 0x1L; + } + return v; + } + + /** + * Get the seconds component of the timestamp. + * + * @return + */ + public long getSeconds() { + return seconds; + } + + /** + * Set the seconds component of the timestamp. + * + * @param seconds + */ + public void setSeconds(long seconds) { + this.seconds = seconds & 0x0000_FFFF_FFFF_FFFFL; + } + + /** + * Get the ticks component of the timestamp. + * + * @return + */ + public int getTicks() { + return ticks; + } + + /** + * Set the ticks component of the timestamp. + * + * @param ticks + */ + public void setTicks(int ticks) { + this.ticks = ticks & 0x7FFF; + } + + /** + * Get the authoritative flag of the timestamp. + * An Authoritative timestamp is one based on a GPS, NTP, Cellular, or other precision time source. + * + * @return + */ + public boolean isAuthoritative() { + return authoritative; + } + + /** + * Set the authoritative flag of the timestamp. + * An Authoritative timestamp is one based on a GPS, NTP, Cellular, or other precision time source. + * + * @param authoritative + */ + public void setAuthoritative(boolean authoritative) { + this.authoritative = authoritative; + } + + @Override + public String toString() { + return String.format("ThreadTimestamp{seconds=%d, ticks=%d, authoritative=%s}", seconds, ticks, + authoritative); + } + } + + /** + * GSON Type adapter for {@link ThreadDataset}. + * + * @author Dan Cunningham - Initial contribution + */ + static class ThreadDatasetAdapter extends TypeAdapter { + + @Override + public void write(@Nullable JsonWriter out, @Nullable ThreadDataset ds) throws IOException { + if (out == null) { + throw new IOException("JsonWriter is null"); + } + if (ds == null) { + out.nullValue(); + return; + } + out.beginObject(); + + if (ds.getActiveTimestamp().isPresent()) { + writeTimestamp(out, TYPE_ACTIVE_TIMESTAMP, ds.getActiveTimestamp().get()); + } + + if (ds.getPendingTimestamp().isPresent()) { + writeTimestamp(out, TYPE_PENDING_TIMESTAMP, ds.getPendingTimestamp().get()); + } + + intOrLong(out, TYPE_DELAY_TIMER, ds.getDelayTimer()); + string(out, TYPE_NETWORK_KEY, ds.getNetworkKeyHex()); + string(out, TYPE_NETWORK_NAME, ds.getNetworkName()); + string(out, TYPE_EXT_PAN_ID, ds.getExtPanIdHex()); + intOrLong(out, TYPE_PAN_ID, ds.getPanId()); + intOrLong(out, TYPE_CHANNEL, ds.getChannel()); + string(out, TYPE_PSKC, ds.getPskcHex()); + intOrLong(out, TYPE_CHANNEL_MASK, ds.getChannelMask()); + + if (ds.getMeshLocalPrefix().isPresent()) { + string(out, TYPE_MESH_LOCAL_PREFIX, ds.getMeshLocalPrefixFormatted()); + } + + if (ds.getSecurityPolicyRotation().isPresent() || ds.getSecurityPolicyFlags().isPresent()) { + out.name(TYPE_SECURITY_POLICY).beginObject(); + out.name(PROP_ROTATION_TIME).value(ds.getSecurityPolicyRotation().orElse(672)); + out.name(PROP_OBTAIN_NETWORK_KEY).value(ds.isObtainNetworkKey()); + out.name(PROP_NATIVE_COMMISSIONING).value(ds.isNativeCommissioning()); + out.name(PROP_ROUTERS).value(ds.isRoutersEnabled()); + out.name(PROP_EXTERNAL_COMMISSIONING).value(ds.isExternalCommissioning()); + out.name(PROP_COMMERCIAL_COMMISSIONING).value(ds.isCommercialCommissioning()); + out.name(PROP_AUTONOMOUS_ENROLLMENT).value(ds.isAutonomousEnrollment()); + out.name(PROP_NETWORK_KEY_PROVISIONING).value(ds.isNetworkKeyProvisioning()); + out.name(PROP_TOBLE_LINK).value(ds.isToBleLink()); + out.name(PROP_NON_CCM_ROUTERS).value(ds.isNonCcmRouters()); + out.endObject(); + } + out.endObject(); + } + + @Override + public @Nullable ThreadDataset read(@Nullable JsonReader in) throws IOException { + if (in == null) { + throw new IOException("JsonReader is null"); + } + ThreadDataset ds = new ThreadDataset(); + + in.beginObject(); + while (in.hasNext()) { + switch (in.nextName()) { + case TYPE_ACTIVE_TIMESTAMP -> readTimestamp(in, ds::setActiveTimestamp); + case TYPE_PENDING_TIMESTAMP -> readTimestamp(in, ds::setPendingTimestamp); + case TYPE_DELAY_TIMER -> ds.setDelayTimer(in.nextLong()); + case TYPE_NETWORK_KEY -> ds.setNetworkKey(in.nextString()); + case TYPE_NETWORK_NAME -> ds.setNetworkName(in.nextString()); + case TYPE_EXT_PAN_ID -> ds.setExtPanId(in.nextString()); + case TYPE_MESH_LOCAL_PREFIX -> ds.setMeshLocalPrefix(parsePrefix(in.nextString())); + case TYPE_PAN_ID -> ds.setPanId(in.nextInt()); + case TYPE_CHANNEL -> ds.setChannel(in.nextInt()); + case TYPE_PSKC -> ds.setPskc(in.nextString()); + case TYPE_CHANNEL_MASK -> ds.setChannelMask(in.nextLong()); + case TYPE_SECURITY_POLICY -> readSecurityPolicy(in, ds); + default -> in.skipValue(); + } + } + in.endObject(); + return ds; + } + + private static void writeTimestamp(JsonWriter out, String name, long ts) throws IOException { + out.name(name).beginObject(); + out.name(PROP_SECONDS).value((ts >>> 16) & 0x0000_FFFF_FFFF_FFFFL); + out.name(PROP_TICKS).value((ts >>> 1) & 0x7FFF); + out.name(PROP_AUTHORITATIVE).value((ts & 0x1L) != 0); + out.endObject(); + } + + private static void readTimestamp(JsonReader in, java.util.function.LongConsumer setter) throws IOException { + long seconds = 0; + int ticks = 0; + boolean auth = false; + in.beginObject(); + while (in.hasNext()) { + switch (in.nextName()) { + case PROP_SECONDS -> seconds = in.nextLong(); + case PROP_TICKS -> ticks = in.nextInt(); + case PROP_AUTHORITATIVE -> auth = in.nextBoolean(); + default -> in.skipValue(); + } + } + in.endObject(); + long ts = (seconds & 0x0000_FFFF_FFFF_FFFFL) << 16; + ts |= ((long) ticks & 0x7FFFL) << 1; + if (auth) { + ts |= 0x1L; + } + setter.accept(ts); + } + + private static void readSecurityPolicy(JsonReader in, ThreadDataset ds) throws IOException { + int rotation = 672; + int flags = 0; + + in.beginObject(); + while (in.hasNext()) { + switch (in.nextName()) { + case PROP_ROTATION_TIME -> rotation = in.nextInt(); + + case PROP_OBTAIN_NETWORK_KEY -> { + if (in.nextBoolean()) { + flags |= SP_OBTAIN_NETWORK_KEY; + } + } + case PROP_NATIVE_COMMISSIONING -> { + if (in.nextBoolean()) { + flags |= SP_NATIVE_COMMISSIONING; + } + } + case PROP_ROUTERS -> { + if (in.nextBoolean()) { + flags |= SP_ROUTERS; + } + } + case PROP_EXTERNAL_COMMISSIONING -> { + if (in.nextBoolean()) { + flags |= SP_EXTERNAL_COMM; + } + } + case PROP_COMMERCIAL_COMMISSIONING -> { + if (in.nextBoolean()) { + flags |= SP_COMMERCIAL_COMM; + } + } + case PROP_AUTONOMOUS_ENROLLMENT -> { + if (in.nextBoolean()) { + flags |= SP_AUTO_ENROLL; + } + } + case PROP_NETWORK_KEY_PROVISIONING -> { + if (in.nextBoolean()) { + flags |= SP_NET_KEY_PROV; + } + } + case PROP_TOBLE_LINK -> { + if (in.nextBoolean()) { + flags |= SP_TO_BLE_LINK; + } + } + case PROP_NON_CCM_ROUTERS -> { + if (in.nextBoolean()) { + flags |= SP_NON_CCM_ROUTERS; + } + } + case "RawFlags" -> flags = Integer.parseInt(in.nextString(), 16); + + default -> in.skipValue(); + } + } + in.endObject(); + + // ensure reserved bits are set + flags |= SP_RSV; + ds.setSecurityPolicy(rotation, flags); + } + + private static void intOrLong(JsonWriter out, String name, Optional n) throws IOException { + if (n.isPresent()) { + out.name(name); + out.value(n.get()); + } + } + + private static void string(JsonWriter out, String name, Optional s) throws IOException { + if (s.isPresent()) { + out.name(name); + out.value(s.get()); + } + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/TlvCodec.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/TlvCodec.java new file mode 100644 index 00000000000..d34dba3dd82 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/TlvCodec.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Optional; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * A utility class for encoding and decoding TLV (Type-Length-Value) data. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class TlvCodec { + + private final Map tlvs; + + public TlvCodec() { + this(new LinkedHashMap<>()); + } + + public TlvCodec(Map existingMap) { + this.tlvs = existingMap; + } + + /** + * Get the bytes for a given TLV type. + * + * @param type the TLV type + * @return the bytes for the TLV type, or empty if the type is not present + */ + public Optional getBytes(int type) { + return Optional.ofNullable(tlvs.get(type)); + } + + /** + * Get the unsigned 16-bit integer value for a given TLV type. + * + * @param type the TLV type + * @return the unsigned 16-bit integer value, or empty if the type is not present + */ + public Optional getUint16(int type) { + return getBytes(type).map(b -> Short.toUnsignedInt(ByteBuffer.wrap(b).getShort())); + } + + /** + * Get the unsigned 32-bit integer value for a given TLV type. + * + * @param type the TLV type + * @return the unsigned 32-bit integer value, or empty if the type is not present + */ + public Optional getUint32(int type) { + return getBytes(type).map(b -> Integer.toUnsignedLong(ByteBuffer.wrap(b).getInt())); + } + + /** + * Get the unsigned 64-bit integer value for a given TLV type. + * + * @param type the TLV type + * @return the unsigned 64-bit integer value, or empty if the type is not present + */ + public Optional getUint64(int type) { + return getBytes(type).map(b -> ByteBuffer.wrap(b).getLong()); + } + + /** + * Put the unsigned 16-bit integer value for a given TLV type. + * + * @param type the TLV type + * @param value the unsigned 16-bit integer value + */ + public void putUint16(int type, int value) { + tlvs.put(type, ByteBuffer.allocate(2).putShort((short) value).array()); + } + + /** + * Put the unsigned 32-bit integer value for a given TLV type. + * + * @param type the TLV type + * @param value the unsigned 32-bit integer value + */ + public void putUint32(int type, long value) { + tlvs.put(type, ByteBuffer.allocate(4).putInt((int) value).array()); + } + + /** + * Put the unsigned 64-bit integer value for a given TLV type. + * + * @param type the TLV type + * @param value the unsigned 64-bit integer value + */ + public void putUint64(int type, long value) { + tlvs.put(type, ByteBuffer.allocate(8).putLong(value).array()); + } + + /** + * Put the bytes for a given TLV type. + * + * @param type the TLV type + * @param value the bytes + */ + public void putBytes(int type, byte[] value) { + tlvs.put(type, value.clone()); + } + + /** + * Put the bytes for a given TLV type with an expected length. + * + * @param type the TLV type + * @param value the bytes + * @param expectedLength the expected length of the bytes + * @throws IllegalArgumentException if the length of the bytes is not equal to the expected length + */ + public void putBytes(int type, byte[] value, int expectedLength) { + if (value.length != expectedLength) { + throw new IllegalArgumentException("TLV " + type + " expects " + expectedLength + " bytes"); + } + putBytes(type, value); + } + + /** + * Get a read-only view of the underlying map. + * + * @return a read-only view of the underlying map + */ + public Map asMap() { + return Collections.unmodifiableMap(tlvs); + } + + /** + * Strip the 0x prefix from a hex string. + * + * @param hex the hex string + * @return the hex string without the 0x prefix + */ + public static String strip0x(String hex) { + return hex.replaceFirst("(?i)^0x", ""); + } + + /** + * Convert a hex string to a byte array. + * + * @param hex the hex string + * @return the byte array + */ + public static byte[] hexStringToBytes(String hex) { + hex = strip0x(hex); + byte[] result = new byte[hex.length() / 2]; + for (int i = 0; i < hex.length(); i += 2) { + result[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16); + } + return result; + } + + /** + * Convert a byte array to a hex string. + * + * @param bytes the byte array + * @return the hex string + */ + public static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(String.format("%02X", b)); + } + return sb.toString(); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/TranslationService.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/TranslationService.java new file mode 100644 index 00000000000..9a956f2ceff --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/TranslationService.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.core.i18n.LocaleProvider; +import org.openhab.core.i18n.TranslationProvider; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; + +/** + * Helper service to get localized strings from the bundle + * + * @author Dan Cunningham - Initial Contribution + */ +@NonNullByDefault +@Component(service = TranslationService.class, scope = ServiceScope.SINGLETON) +public class TranslationService { + private final BundleContext bundleContext; + private final Bundle bundle; + private final LocaleProvider localeProvider; + private final TranslationProvider translationProvider; + + @Activate + public TranslationService(@Reference LocaleProvider localeProvider, + @Reference TranslationProvider translationProvider) { + this.bundleContext = FrameworkUtil.getBundle(TranslationService.class).getBundleContext(); + this.bundle = bundleContext.getBundle(); + this.localeProvider = localeProvider; + this.translationProvider = translationProvider; + } + + /** + * Get a translation for a given key + * + * @param key the key to get the translation for (with or without the @text/ prefix) + * @return the translation + */ + public String getTranslation(String key) { + String lookupKey = key.replace("@text/", ""); + String result = translationProvider.getText(bundle, lookupKey, lookupKey, localeProvider.getLocale()); + return result == null ? lookupKey : result; + } + + public LocaleProvider getLocaleProvider() { + return localeProvider; + } + + public TranslationProvider getTranslationProvider() { + return translationProvider; + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/ValueUtils.java b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/ValueUtils.java new file mode 100644 index 00000000000..c12ba038e2f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/java/org/openhab/binding/matter/internal/util/ValueUtils.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.types.Type; + +/** + * Utility class for converting values to and from Matter types. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class ValueUtils { + private static final BigDecimal TEMPERATURE_MULTIPLIER = new BigDecimal(100); + + /** + * Converts a ZigBee 8 bit level as used in Level Control cluster and others to a percentage + * + * @param level an integer between 0 and 254 + * @return the scaled {@link PercentType} + */ + public static PercentType levelToPercent(int level) { + int result = (int) Math.round(level * 100.0 / 254.0); + return level == 0 ? PercentType.ZERO : new PercentType(Math.max(result, 1)); + } + + /** + * Converts a {@link PercentType} to an 8 bit level scaled between 0 and 254 + * + * @param percent the {@link PercentType} to convert + * @return a scaled value between 0 and 254 + */ + + public static int percentToLevel(PercentType percent) { + return (int) (percent.floatValue() * 254.0f / 100.0f + 0.5f); + } + + /** + * Converts a {@link Command} to a ZigBee / Matter temperature integer + * + * @param type the {@link Type} to convert + * @return the {@link Type} or null if the conversion was not possible + */ + public static @Nullable Integer temperatureToValue(Type type) { + BigDecimal value = null; + if (type instanceof QuantityType quantity) { + if (quantity.getUnit() == SIUnits.CELSIUS) { + value = quantity.toBigDecimal(); + } else if (quantity.getUnit() == ImperialUnits.FAHRENHEIT) { + QuantityType celsius = quantity.toUnit(SIUnits.CELSIUS); + if (celsius != null) { + value = celsius.toBigDecimal(); + } + } + } else if (type instanceof Number number) { + // No scale, so assumed to be Celsius + value = BigDecimal.valueOf(number.doubleValue()); + } + if (value == null) { + return null; + } + // originally this used RoundingMode.CEILING, if there are accuracy problems, we may want to revisit that + return value.setScale(2, RoundingMode.HALF_UP).multiply(TEMPERATURE_MULTIPLIER).intValue(); + } + + /** + * Converts an integer value into a {@link QuantityType}. The temperature as an integer is assumed to be multiplied + * by 100 as per the ZigBee / Matter standard format. + * + * @param value the integer value to convert + * @return the {@link QuantityType} + */ + public static QuantityType valueToTemperature(int value) { + return new QuantityType<>(BigDecimal.valueOf(value, 2), SIUnits.CELSIUS); + } +} diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/addon/addon.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/addon/addon.xml new file mode 100644 index 00000000000..957732a0b17 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/addon/addon.xml @@ -0,0 +1,12 @@ + + + + binding + Matter Binding + This is the binding for Matter. + local + org.openhab.matter + + diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/config/config.xml new file mode 100644 index 00000000000..498941c0581 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/config/config.xml @@ -0,0 +1,65 @@ + + + + + + + false + + + + Enable Matter bridge functionality to expose items to other Matter clients. + true + + + + Current running state of the Matter bridge. + + + + Name of the Matter bridge that is exposed to clients. + openHAB Matter Bridge + + + + qrcode + QR code used for pairing 3rd party Matter clients. + + + + Manual code used for pairing 3rd party Matter clients. + + + + This will open the commissioning window for 15 minutes, allowing pairings with 3rd party clients. + false + + + + Defines the Matter bridge passcode, used for commissioning the first client. + 20202021 + + + + Defines the Matter discriminator which uniquely identifies this bridge on the network. Used for + commissioning clients. + true + + + + Defines the port the Matter bridge listens on. + 5540 + true + + + + This will reset the Matter bridge, removing all pairings with 3rd party clients. + false + true + + + diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties new file mode 100644 index 00000000000..ee56ca5130b --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/i18n/matter.properties @@ -0,0 +1,326 @@ +# add-on + +addon.matter.name = Matter Binding +addon.matter.description = This is the binding for Matter. + +# add-on config + +io.config.matter.bridgeName.label = Bridge name +io.config.matter.bridgeName.description = Name of the Matter bridge that is exposed to clients. +io.config.matter.discriminator.label = Discriminator +io.config.matter.discriminator.description = Defines the Matter discriminator which uniquely identifies this bridge on the network. Used for commissioning clients. +io.config.matter.enableBridge.label = Enable Matter Bridge +io.config.matter.enableBridge.description = Enable Matter bridge functionality to expose items to other Matter clients. +io.config.matter.group.bridge.label = Matter Bridge Configuration. +io.config.matter.manualPairingCode.label = Matter Manual Pairing Code +io.config.matter.manualPairingCode.description = Manual code used for pairing 3rd party Matter clients. +io.config.matter.openCommissioningWindow.label = Allow Commissioning +io.config.matter.openCommissioningWindow.description = This will open the commissioning window for 15 minutes, allowing pairings with 3rd party clients. +io.config.matter.passcode.label = Passcode +io.config.matter.passcode.description = Defines the Matter bridge passcode, used for commissioning the first client. +io.config.matter.port.label = Port +io.config.matter.port.description = Defines the port the Matter bridge listens on. +io.config.matter.qrCode.label = Matter QR Pairing Code +io.config.matter.qrCode.description = QR code used for pairing 3rd party Matter clients. +io.config.matter.resetBridge.label = Reset Bridge +io.config.matter.resetBridge.description = This will reset the Matter bridge, removing all pairings with 3rd party clients. +io.config.matter.runningState.label = Running State +io.config.matter.runningState.description = Current running state of the Matter bridge. + +# thing types + +thing-type.matter.controller.label = Matter Controller +thing-type.matter.controller.description = The Matter controller connects to Matter nodes and devices +thing-type.matter.endpoint.label = Matter Endpoint +thing-type.matter.endpoint.description = A Matter Endpoint represented as a standalone Thing +thing-type.matter.node.label = Matter Node +thing-type.matter.node.description = The Matter node describes a logical node in a Matter Fabric + +# thing types config + +thing-type.config.matter.controller.nodeId.label = Node ID +thing-type.config.matter.controller.nodeId.description = The Matter Node ID / Number of the Controller bridge. Do not change this once devices have been added. +thing-type.config.matter.endpoint.endpointId.label = Endpoint Id +thing-type.config.matter.endpoint.endpointId.description = The Matter Endpoint Id +thing-type.config.matter.endpoint.pollInterval.label = Poll Interval (seconds) +thing-type.config.matter.endpoint.pollInterval.description = The interval to poll in seconds for diagnostic cluster attributes. Set to 0 to disable polling. +thing-type.config.matter.node.nodeId.label = Node Id +thing-type.config.matter.node.nodeId.description = The Matter Node Id +thing-type.config.matter.node.pollInterval.label = Poll Interval (seconds) +thing-type.config.matter.node.pollInterval.description = The interval to poll in seconds for diagnostic cluster attributes. Set to 0 to disable polling. + +# channel types + +channel-type.matter.battery-alarm.label = Battery Alarm +channel-type.matter.battery-alarm.description = The battery alarm state +channel-type.matter.battery-alarm.state.option.minThreshold = Below min threshold +channel-type.matter.battery-alarm.state.option.threshold1 = Below threshold 1 +channel-type.matter.battery-alarm.state.option.threshold2 = Below threshold 2 +channel-type.matter.battery-alarm.state.option.threshold3 = Below threshold 3 +channel-type.matter.battery-alarm.state.option.noThreshold = No battery alarm +channel-type.matter.battery-voltage.label = Battery Voltage +channel-type.matter.battery-voltage.description = The current battery voltage +channel-type.matter.booleanstate-statevalue.label = Boolean State +channel-type.matter.booleanstate-statevalue.description = Indicates a boolean, true or false, value +channel-type.matter.colorcontrol-color.label = Color +channel-type.matter.colorcontrol-color.description = The color channel allows to control the color of a light. It is also possible to dim values and switch the light on and off. +channel-type.matter.colorcontrol-temperature-abs.label = Color Temperature +channel-type.matter.colorcontrol-temperature-abs.description = Sets the color temperature of the light in mirek +channel-type.matter.colorcontrol-temperature.label = Color Temperature +channel-type.matter.colorcontrol-temperature.description = Sets the color temperature of the light +channel-type.matter.doorlock-lockstate.label = Door Lock State +channel-type.matter.doorlock-lockstate.description = Locks and unlocks the door and maintains the lock state +channel-type.matter.electricalenergymeasurement-energymeasurmement-energy.label = Energy +channel-type.matter.electricalpowermeasurement-activecurrent.label = Active Current +channel-type.matter.electricalpowermeasurement-activepower.label = Active Power +channel-type.matter.electricalpowermeasurement-voltage.label = Voltage +channel-type.matter.fancontrol-fanmode.label = Fan Mode +channel-type.matter.fancontrol-fanmode.description = Sets the fan mode +channel-type.matter.fancontrol-fanmode.state.option.0 = Off +channel-type.matter.fancontrol-fanmode.state.option.1 = Low +channel-type.matter.fancontrol-fanmode.state.option.2 = Medium +channel-type.matter.fancontrol-fanmode.state.option.3 = High +channel-type.matter.fancontrol-fanmode.state.option.4 = On +channel-type.matter.fancontrol-fanmode.state.option.5 = Auto +channel-type.matter.fancontrol-percent.label = Fan Speed +channel-type.matter.fancontrol-percent.description = The current fan speed percentage +channel-type.matter.illuminancemeasurement-measuredvalue.label = Illumination +channel-type.matter.illuminancemeasurement-measuredvalue.description = Indicates the brightness in Lux. +channel-type.matter.levelcontrol-level.label = Dimmer +channel-type.matter.levelcontrol-level.description = Sets the level of the light +channel-type.matter.modeselect-mode.label = Mode Select +channel-type.matter.modeselect-mode.description = Selection of 1 or more states +channel-type.matter.occupancysensing-occupied.label = Occupancy +channel-type.matter.occupancysensing-occupied.description = Indicates if an occupancy sensor is triggered +channel-type.matter.onoffcontrol-onoff.label = Switch +channel-type.matter.onoffcontrol-onoff.description = Switches the power on and off +channel-type.matter.powersource-batchargelevel.label = Battery Charge Level +channel-type.matter.powersource-batchargelevel.description = Indicates a coarse ranking of the charge level of the battery, used to indicate when intervention is required +channel-type.matter.powersource-batchargelevel.state.option.0 = Ok +channel-type.matter.powersource-batchargelevel.state.option.1 = Warning +channel-type.matter.powersource-batchargelevel.state.option.2 = Critical +channel-type.matter.powersource-batpercentremaining.label = Battery Percent Remaining +channel-type.matter.powersource-batpercentremaining.description = Indicates the estimated percentage of battery charge remaining until the battery will no longer be able to provide power to the Node +channel-type.matter.relativehumiditymeasurement-measuredvalue.label = Humidity +channel-type.matter.relativehumiditymeasurement-measuredvalue.description = Indicates the current relative humidity +channel-type.matter.switch-initialpress.label = Initial Press Trigger +channel-type.matter.switch-initialpress.description = This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. while pressed. +channel-type.matter.switch-longpress.label = Long Press Trigger +channel-type.matter.switch-longpress.description = This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. while long pressed. +channel-type.matter.switch-longrelease.label = Long Release Trigger +channel-type.matter.switch-longrelease.description = This trigger shall indicate the previous value of the CurrentPosition attribute as a JSON object, i.e. just prior to a long release. +channel-type.matter.switch-multipresscomplete.label = Multi-Press Complete Trigger +channel-type.matter.switch-multipresscomplete.description = This trigger shall indicate 2 numeric fields as a JSON object. The first is the new value of the CurrentPosition attribute, i.e. while pressed. The second is how many times the momentary switch has been pressed in a multi-press sequence. +channel-type.matter.switch-multipressongoing.label = Multi-Press Ongoing Trigger +channel-type.matter.switch-multipressongoing.description = This trigger shall indicate 2 numeric fields as a JSON object. The first is the new value of the CurrentPosition attribute, i.e. while pressed. The second is the multi press code with a value of N when the Nth press of a multi-press sequence has been detected. +channel-type.matter.switch-shortrelease.label = Short Release Trigger +channel-type.matter.switch-shortrelease.description = This trigger shall indicate the previous value of the CurrentPosition attribute as a JSON object, i.e. just prior to a short release. +channel-type.matter.switch-switch.label = Switch +channel-type.matter.switch-switch.description = Indication of a switch or remote being activated +channel-type.matter.switch-switchlatched.label = Switched Latched Trigger +channel-type.matter.switch-switchlatched.description = This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. after the move. +channel-type.matter.temperaturemeasurement-measuredvalue.label = Temperature +channel-type.matter.temperaturemeasurement-measuredvalue.description = Indicates the temperature reading +channel-type.matter.thermostat-localtemperature.label = Local Temperature +channel-type.matter.thermostat-localtemperature.description = Indicates the local temperature provided by the thermostat +channel-type.matter.thermostat-occupiedcooling.label = Occupied Cooling Setpoint +channel-type.matter.thermostat-occupiedcooling.description = Sets the cooling temperature when the room is occupied +channel-type.matter.thermostat-occupiedheating.label = Occupied Heating Setpoint +channel-type.matter.thermostat-occupiedheating.description = Sets the heating temperature when the room is occupied +channel-type.matter.thermostat-outdoortemperature.label = Outdoor Temperature +channel-type.matter.thermostat-outdoortemperature.description = Indicates the outdoor temperature provided by the thermostat +channel-type.matter.thermostat-runningmode.label = Running Mode +channel-type.matter.thermostat-runningmode.description = The running mode of the thermostat +channel-type.matter.thermostat-runningmode.state.option.0 = Off +channel-type.matter.thermostat-runningmode.state.option.3 = Cool +channel-type.matter.thermostat-runningmode.state.option.4 = Heat +channel-type.matter.thermostat-systemmode.label = System Mode +channel-type.matter.thermostat-systemmode.description = Sets the system mode of the thermostat +channel-type.matter.thermostat-systemmode.state.option.0 = Off +channel-type.matter.thermostat-systemmode.state.option.1 = Auto +channel-type.matter.thermostat-systemmode.state.option.3 = Cool +channel-type.matter.thermostat-systemmode.state.option.4 = Heat +channel-type.matter.thermostat-systemmode.state.option.5 = Emergency Heating +channel-type.matter.thermostat-systemmode.state.option.6 = Precooling +channel-type.matter.thermostat-systemmode.state.option.7 = Fan Only +channel-type.matter.thermostat-systemmode.state.option.8 = Dry +channel-type.matter.thermostat-systemmode.state.option.9 = Sleep +channel-type.matter.thermostat-unoccupiedcooling.label = Unoccupied Cooling Setpoint +channel-type.matter.thermostat-unoccupiedcooling.description = Sets the cooling temperature when the room is unoccupied +channel-type.matter.thermostat-unoccupiedheating.label = Unoccupied Heating Setpoint +channel-type.matter.thermostat-unoccupiedheating.description = Sets the heating temperature when the room is unoccupied +channel-type.matter.wifinetworkdiagnostics-rssi.label = Signal +channel-type.matter.wifinetworkdiagnostics-rssi.description = Wi-Fi signal strength indicator. +channel-type.matter.windowcovering-lift.label = Window Covering Lift +channel-type.matter.windowcovering-lift.description = Sets the window covering level - supporting open/close and up/down type commands + +# thread border router configuration + +thing-type.config.matter.node.thread_border_router_operational_dataset.label = Thread Border Router Operational Dataset +thing-type.config.matter.node.thread_border_router_operational_dataset.description = Thread border router operational dataset values. Use actions to push changes to the device after saving. +thing-type.config.matter.node.thread_network_channel_number.label = Thread Network Channel Number +thing-type.config.matter.node.thread_network_channel_number.description = The thread network channel number to set (11-26) +thing-type.config.matter.node.thread_network_allowed_channels.label = Thread Network Allowed Channels +thing-type.config.matter.node.thread_network_allowed_channels.description = A comma separated list of channel numbers allowed on the Thread network (ex: 11,12,13). Recommended to include all channels (11-26) +thing-type.config.matter.node.thread_extended_pan_id.label = Thread Extended PAN ID +thing-type.config.matter.node.thread_extended_pan_id.description = The thread extended PAN ID to set (ex: 11111111222222AB) +thing-type.config.matter.node.thread_mesh_local_prefix.label = Thread Mesh Local Prefix +thing-type.config.matter.node.thread_mesh_local_prefix.description = The thread mesh-local prefix to set (ex: fd11:22::/64) +thing-type.config.matter.node.thread_network_name.label = Thread Network Name +thing-type.config.matter.node.thread_network_name.description = The thread network name to set (ex: openHAB-Thread) +thing-type.config.matter.node.thread_network_key.label = Thread Network Key +thing-type.config.matter.node.thread_network_key.description = The thread network key to set, leave blank to use a random secure key (ex: 00112233445566778899aabbccddeeff) +thing-type.config.matter.node.thread_pan_id.label = Thread PAN ID +thing-type.config.matter.node.thread_pan_id.description = The thread PAN ID to set (ex: 1234) +thing-type.config.matter.node.thread_pskc.label = Thread PSKC +thing-type.config.matter.node.thread_pskc.description = The thread PSKC to set (hex) +thing-type.config.matter.node.thread_active_timestamp_seconds.label = Thread Active Timestamp Seconds +thing-type.config.matter.node.thread_active_timestamp_seconds.description = The thread encoded active timestamp seconds +thing-type.config.matter.node.thread_active_timestamp_ticks.label = Thread Active Timestamp Ticks +thing-type.config.matter.node.thread_active_timestamp_ticks.description = The thread encoded active timestamp ticks +thing-type.config.matter.node.thread_active_timestamp_is_authoritative.label = Thread Active Timestamp Is Authoritative +thing-type.config.matter.node.thread_active_timestamp_is_authoritative.description = The thread encoded active timestamp is authoritative (GPS, NTP, Cellular, etc.) +thing-type.config.matter.node.thread_dataset_security_policy.label = Thread Dataset Security Policy +thing-type.config.matter.node.thread_dataset_security_policy.description = Thread dataset security policy values. Default values recommended for most use cases. +thing-type.config.matter.node.security_policy_rotation_time.label = Security Policy Rotation Time +thing-type.config.matter.node.security_policy_rotation_time.description = Security Policy Rotation Time (hours). Defaults to 672. +thing-type.config.matter.node.security_policy_obtain_network_key.label = Security Policy: Obtain Network Key +thing-type.config.matter.node.security_policy_obtain_network_key.description = Security Policy: Obtain Network Key. Defaults to true. +thing-type.config.matter.node.security_policy_native_commissioning.label = Security Policy: Native Commissioning +thing-type.config.matter.node.security_policy_native_commissioning.description = Security Policy: Native Commissioning. Defaults to true. +thing-type.config.matter.node.security_policy_routers.label = Security Policy: Routers Enabled +thing-type.config.matter.node.security_policy_routers.description = Security Policy: Routers Enabled. Defaults to true. +thing-type.config.matter.node.security_policy_external_commissioning.label = Security Policy: External Commissioning +thing-type.config.matter.node.security_policy_external_commissioning.description = Security Policy: External Commissioning. Defaults to true. +thing-type.config.matter.node.security_policy_commercial_commissioning.label = Security Policy: Commercial Commissioning +thing-type.config.matter.node.security_policy_commercial_commissioning.description = Security Policy: Commercial Commissioning. Defaults to false. +thing-type.config.matter.node.security_policy_autonomous_enrollment.label = Security Policy: Autonomous Enrollment +thing-type.config.matter.node.security_policy_autonomous_enrollment.description = Security Policy: Autonomous Enrollment. Defaults to true. +thing-type.config.matter.node.security_policy_network_key_provisioning.label = Security Policy: Network Key Provisioning +thing-type.config.matter.node.security_policy_network_key_provisioning.description = Security Policy: Network Key Provisioning. Defaults to true. +thing-type.config.matter.node.security_policy_to_ble_link.label = Security Policy: TO BLE Link +thing-type.config.matter.node.security_policy_to_ble_link.description = Security Policy: TO BLE Link. Defaults to true. +thing-type.config.matter.node.security_policy_non_ccm_routers.label = Security Policy: Non-CCM Routers +thing-type.config.matter.node.security_policy_non_ccm_routers.description = Security Policy: Non-CCM Routers. Defaults to false. + +# discovery + +discovery.matter.scan-input.label = Matter Pairing Code +discovery.matter.scan-input.description = 11 digit matter pairing code (with or without hyphens) or a short code and key (separated by a space) +discovery.matter.bridge-endpoint.label = Matter Endpoint (Bridged): {0} +discovery.matter.node-device.label = Matter Device: {0} +discovery.matter.unknown-node.label = Matter Device: Unknown + +# thing actions + +thing-action.node.generateNewPairingCode.label = Generate a new pairing code for a Matter device +thing-action.node.generateNewPairingCode.description = Generates a new manual and QR pairing code to be used to pair the Matter device with an external Matter controller +thing-action.node.generateNewPairingCode.manual-pairing-code.label = Manual pairing code +thing-action.node.generateNewPairingCode.qr-pairing-code.label = QR pairing code +thing-action.node.decommission.label = Decommission Matter node from fabric +thing-action.node.decommission.description = This will remove the device from the Matter fabric. If the device is online and reachable this will attempt to remove the credentials from the device first before removing it from the network. Once a device is removed, this Thing will go offline and can be removed. +thing-action.node.decommission.result.label = Result from decommissioning process +thing-action.node.getFabrics.label = List connected Matter fabrics +thing-action.node.getFabrics.description = This will list all the Matter fabrics this node belongs to +thing-action.node.getFabrics.result.label = Connected Fabrics +thing-action.node.removeFabric.label = Remove connected Matter fabric +thing-action.node.removeFabric.description = This removes a connected Matter fabric from a device. Use the 'List connected Matter fabrics' action to retrieve the fabric index number +thing-action.node.removeFabric.result.label = Remove Result +thing-action.node.removeFabric.index.label = The index number of the fabric +thing-action.node.removeFabric.index.description = The index number of the connected Matter fabric + +# action results + +thing-action.result.success = Success +thing-action.result.no-handler = Error: No handler found +thing-action.result.no-fabrics = No fabrics found +thing-action.result.no-converter = Error: No converter found +thing-action.result.invalid-json = Error: Invalid JSON dataset +thing-action.result.error-generating-key = Error generating master key +thing-action.result.error-setting-dataset = Error setting active dataset + +# matter otbr actions + +thing-action.otbr.generateDataset.label = Thread: Operational Dataset Generator +thing-action.otbr.generateDataset.description = Generates a new operational dataset and optionally saves it locally. +thing-action.otbr.generateDataset.result.label = Set Result +thing-action.otbr.generateDataset.json.label = Operational Dataset JSON +thing-action.otbr.generateDataset.hex.label = Operational Dataset Hex +thing-action.otbr.generateDataset.save.label = Save new operational dataset +thing-action.otbr.generateDataset.save.description = Both return the JSON dataset, and save the dataset to this Thing's configuration. To push the dataset to the device, use the 'Push local operational dataset' action after committing. +thing-action.otbr.generateDataset.channel.label = Thread network channel number +thing-action.otbr.generateDataset.channel.description = The thread network channel number (11-26) +thing-action.otbr.generateDataset.timestampSeconds.label = Thread active timestamp seconds +thing-action.otbr.generateDataset.timestampSeconds.description = The thread active timestamp seconds +thing-action.otbr.generateDataset.timestampTicks.label = Thread active timestamp ticks +thing-action.otbr.generateDataset.timestampTicks.description = The thread active timestamp ticks +thing-action.otbr.generateDataset.timestampAuthoritative.label = Thread active timestamp is authoritative +thing-action.otbr.generateDataset.timestampAuthoritative.description = The thread active timestamp is authoritative +thing-action.otbr.generateDataset.panId.label = Thread PAN ID +thing-action.otbr.generateDataset.panId.description = The thread PAN ID to (1-65535) +thing-action.otbr.generateDataset.extendedPanId.label = Thread extended PAN ID +thing-action.otbr.generateDataset.extendedPanId.description = The thread extended PAN ID in hex format(16 characters) +thing-action.otbr.generateDataset.meshPrefix.label = Thread mesh-local prefix +thing-action.otbr.generateDataset.meshPrefix.description = The thread mesh-local prefix +thing-action.otbr.generateDataset.networkName.label = Thread network name +thing-action.otbr.generateDataset.networkName.description = The thread network name +thing-action.otbr.generateDataset.networkKey.label = Thread network key +thing-action.otbr.generateDataset.networkKey.description = The thread network key. Leave blank to auto generate a secure key +thing-action.otbr.generateDataset.passphrase.label = Passphrase/Commissioner Credential +thing-action.otbr.generateDataset.passphrase.description = The thread Passphrase/Commissioner Credential +thing-action.otbr.generateDataset.rotationTime.label = Security Policy Rotation Time +thing-action.otbr.generateDataset.rotationTime.description = Security Policy Rotation Time (hours). Defaults to 672. +thing-action.otbr.generateDataset.obtainNetworkKey.label = Security Policy: Obtain Network Key +thing-action.otbr.generateDataset.obtainNetworkKey.description = Security Policy: Obtain Network Key. Defaults to true. +thing-action.otbr.generateDataset.nativeCommissioning.label = Security Policy: Native Commissioning +thing-action.otbr.generateDataset.nativeCommissioning.description = Security Policy: Native Commissioning. Defaults to true. +thing-action.otbr.generateDataset.routers.label = Security Policy: Routers +thing-action.otbr.generateDataset.routers.description = Security Policy: Routers Enabled. Defaults to true. +thing-action.otbr.generateDataset.externalCommissioning.label = Security Policy: External Commissioning +thing-action.otbr.generateDataset.externalCommissioning.description = Security Policy: External Commissioning. Defaults to true. +thing-action.otbr.generateDataset.commercialCommissioning.label = Security Policy: Commercial Commissioning +thing-action.otbr.generateDataset.commercialCommissioning.description = Security Policy: Commercial Commissioning. Defaults to false. +thing-action.otbr.generateDataset.autonomousEnrollment.label = Security Policy: Autonomous Enrollment +thing-action.otbr.generateDataset.autonomousEnrollment.description = Security Policy: Autonomous Enrollment. Defaults to true. +thing-action.otbr.generateDataset.networkKeyProvisioning.label = Security Policy: Network Key Provisioning +thing-action.otbr.generateDataset.networkKeyProvisioning.description = Security Policy: Network Key Provisioning. Defaults to true. +thing-action.otbr.generateDataset.tobleLink.label = Security Policy: TO BLE Link +thing-action.otbr.generateDataset.tobleLink.description = Security Policy: TO BLE Link. Defaults to true. +thing-action.otbr.generateDataset.nonCcmRouters.label = Security Policy: Non-CCM Routers +thing-action.otbr.generateDataset.nonCcmRouters.description = Security Policy: Non-CCM Routers. Defaults to false. +thing-action.otbr.loadActiveDataset.label = Thread: Load operational dataset from device +thing-action.otbr.loadActiveDataset.description = Updates the local operational dataset configuration from the device. +thing-action.otbr.loadActiveDataset.result.label = Set Result +thing-action.otbr.loadActiveDataset.dataset.label = Operational Dataset (Hex) +thing-action.otbr.loadExternalDataset.label = Thread: Load external operational dataset +thing-action.otbr.loadExternalDataset.description = Updates the local operational dataset configuration from a hex or JSON string for the node. Use the 'Push local operational dataset' action to push the dataset back to the device after loading. +thing-action.otbr.loadExternalDataset.dataset.label = Thread operational dataset +thing-action.otbr.loadExternalDataset.dataset.description = The thread operational dataset to set (hex or JSON) +thing-action.otbr.loadExternalDataset.result.label = Set Result +thing-action.otbr.pushDataset.label = Thread: Push local operational dataset +thing-action.otbr.pushDataset.description = Pushes the local operational dataset configuration to the device. +thing-action.otbr.pushDataset.result.label = Set Result +thing-action.otbr.pushDataset.delay.label = Pending Delay +thing-action.otbr.pushDataset.delay.description = The delay in milliseconds before the pending dataset is active. Required to be a minimum of 30 seconds (30000ms), use a longer value of 5 minutes or more if battery powered (sleepy) devices are present to ensure all device are notified of the new dataset. +thing-action.otbr.pushDataset.generateTime.label = Generate New Pending Timestamp +thing-action.otbr.pushDataset.generateTime.description = Generate a new pending timestamp based on the current time. +thing-action.otbr.pushDataset.incrementTime.label = Increment Active Timestamp +thing-action.otbr.pushDataset.incrementTime.description = Increment the active timestamp by x seconds, required to commit the dataset if the existing active timestamp has the same value. + +# matter controller actions + +thing-action.controller.pairDevice.label = Pair a Matter device +thing-action.controller.pairDevice.description = Pairs a Matter device using a manual pairing code or QR code +thing-action.controller.pairDevice.code.label = Manual pairing code or QR code +thing-action.controller.pairDevice.code.description = The pairing code to use for pairing the device +thing-action.controller.pairDevice.result.label = The pairing result + +# action results + +thing-action.result.device-added = Device added to Inbox +thing-action.result.pairing-failed = Failed to pair device: {0} + +# thing status + +thing-status.detail.controller.waitingForData = Waiting for data +thing-status.detail.endpoint.thingNotReachable = Bridge reports device as not reachable diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml new file mode 100644 index 00000000000..c3b3b473c96 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/channels.xml @@ -0,0 +1,477 @@ + + + + + Number:ElectricPotential + + The current battery voltage + Energy + + + + + String + + The battery alarm state + Energy + + Measurement + Voltage + + + + + + + + + + + + + + Number:Dimensionless + + Indicates the estimated percentage of battery charge remaining until the battery will no longer be able + to provide power to the Node + Energy + + Measurement + Energy + + + + + + Number + + Indicates a coarse ranking of the charge level of the battery, used to indicate when intervention is + required + Energy + + Alarm + LowBattery + + + + + + + + + + + + Color + + The color channel allows to control the color of a light. + It is also possible to dim values and switch the + light on and off. + + ColorLight + + Control + Color + + veto + + + + Dimmer + + Sets the color temperature of the light + ColorLight + + Control + ColorTemperature + + + + + + Number:Temperature + + Sets the color temperature of the light in mirek + ColorLight + + Control + ColorTemperature + + + + + + Switch + + Switches the power on and off + Light + + Switch + Power + + veto + + + + Dimmer + + Sets the level of the light + Light + + Control + Brightness + + + veto + + + + Number + + Selection of 1 or more states + + Control + Mode + + + + + + Number + + Indication of a switch or remote being activated + + + + + trigger + + This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. after + the move. + + + + trigger + + This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. while + pressed. + + + + trigger + + This trigger shall indicate the new value of the CurrentPosition attribute as a JSON object, i.e. while + long pressed. + + + + trigger + + This trigger shall indicate the previous value of the CurrentPosition attribute as a JSON object, i.e. + just prior to a short release. + + + + trigger + + This trigger shall indicate the previous value of the CurrentPosition attribute as a JSON object, i.e. + just prior to a long release. + + + + trigger + + This trigger shall indicate 2 numeric fields as a JSON object. The first is the new value of the + CurrentPosition attribute, i.e. while pressed. The second is the multi press code with a value of N when the Nth + press of a multi-press sequence has been detected. + + + + + trigger + + This trigger shall indicate 2 numeric fields as a JSON object. The first is the new value of the + CurrentPosition attribute, i.e. while pressed. The second is how many times the momentary switch has been pressed in + a multi-press sequence. + + + + + Number:Temperature + + Indicates the local temperature provided by the thermostat + Temperature + + Measurement + Temperature + + + + + + Number:Temperature + + Indicates the outdoor temperature provided by the thermostat + Temperature + + Measurement + Temperature + + + + + + Number:Temperature + + Sets the heating temperature when the room is occupied + Temperature + + Setpoint + Temperature + + + + + + Number:Temperature + + Sets the cooling temperature when the room is occupied + Temperature + + Setpoint + Temperature + + + + + + Number:Temperature + + Sets the heating temperature when the room is unoccupied + Temperature + + Setpoint + Temperature + + + + + + Number:Temperature + + Sets the cooling temperature when the room is unoccupied + Temperature + + Setpoint + Temperature + + + + + + Number + + Sets the system mode of the thermostat + + Control + Mode + + + + + + + + + + + + + + + + + + Number + + The running mode of the thermostat + + Status + Mode + + + + + + + + + + + + Rollershutter + + Sets the window covering level - supporting open/close and up/down type commands + + Blinds + + + + Number + + Sets the fan mode + Fan + + Control + Mode + + + + + + + + + + + + + + + Dimmer + + The current fan speed percentage + Fan + + Control + Speed + + + + + + Number:Temperature + + Indicates the temperature reading + Temperature + + Measurement + Temperature + + + + + + Number:Dimensionless + + Indicates the current relative humidity + Humidity + + Measurement + Humidity + + + + + + Switch + + Indicates if an occupancy sensor is triggered + Presence + + Status + Presence + + + + + + Number:Illuminance + + Indicates the brightness in Lux. + + Measurement + Illuminance + + + + + + Switch + + Indicates a boolean, true or false, value + + + + + Number:Power + + Wi-Fi signal strength indicator. + QualityOfService + + Status + RSSI + + + + + + Switch + + Locks and unlocks the door and maintains the lock state + + Status + OpenState + + veto + + + + + Number:Energy + + Energy + + Measurement + Energy + + + + + + Number:Power + + Energy + + Measurement + Power + + + + + + Number:ElectricCurrent + + Energy + + Measurement + Current + + + + + + Number:ElectricPotential + + Energy + + Measurement + Voltage + + + + + diff --git a/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/things.xml b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/things.xml new file mode 100644 index 00000000000..70fe5002c13 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/main/resources/OH-INF/thing/things.xml @@ -0,0 +1,61 @@ + + + + + + The Matter controller connects to Matter nodes and devices + + + + The Matter Node ID / Number of the Controller bridge. Do not change this once devices have been added. + 0 + true + + + + + + + + + + The Matter node describes a logical node in a Matter Fabric + nodeId + + + + The Matter Node Id + + + + The interval to poll in seconds for diagnostic cluster attributes. Set to 0 to disable polling. + true + 1800 + + + + + + + + + + A Matter Endpoint represented as a standalone Thing + endpointId + + + + The Matter Endpoint Id + + + + The interval to poll in seconds for diagnostic cluster attributes. Set to 0 to disable polling. + true + 1800 + + + + diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/ColorDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/ColorDeviceTest.java new file mode 100644 index 00000000000..96b6d5aee82 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/ColorDeviceTest.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.ColorItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; + +/** + * Test class for ColorDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ColorDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + @NonNullByDefault({}) + private ColorItem colorItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private ColorDevice device; + @NonNullByDefault({}) + private HSBType initialHSBState; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + colorItem = Mockito.spy(new ColorItem("test")); + initialHSBState = new HSBType(new DecimalType(0), new PercentType(0), new PercentType(50)); + when(colorItem.getStateAs(HSBType.class)).thenReturn(initialHSBState); + device = new ColorDevice(metadataRegistry, client, colorItem); + } + + @Test + void testDeviceType() { + assertEquals("ColorLight", device.deviceType()); + } + + @Test + void testHandleMatterEventOnOff() { + device.handleMatterEvent("onOff", "onOff", true); + verify(colorItem).send(OnOffType.ON); + + device.handleMatterEvent("onOff", "onOff", false); + verify(colorItem).send(OnOffType.OFF); + } + + @Test + void testHandleMatterEventColor() { + // Turn device on first and wait for future completion + device.handleMatterEvent("onOff", "onOff", true); + verify(colorItem).send(OnOffType.ON); + device.updateState(colorItem, initialHSBState); + + device.handleMatterEvent("colorControl", "currentHue", Double.valueOf(127)); + device.handleMatterEvent("colorControl", "currentSaturation", Double.valueOf(127)); + + verify(colorItem).send(new HSBType(new DecimalType(180), new PercentType(50), new PercentType(50))); + } + + @Test + void testHandleMatterEventLevel() { + device.handleMatterEvent("levelControl", "currentLevel", Double.valueOf(127)); + verify(colorItem).send(new PercentType(50)); + } + + @Test + void testUpdateStateWithHSB() { + HSBType hsb = new HSBType(new DecimalType(180), new PercentType(100), new PercentType(100)); + device.updateState(colorItem, hsb); + + verify(client).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(true)); + verify(client).setEndpointState(any(), eq("levelControl"), eq("currentLevel"), eq(254)); + verify(client).setEndpointState(any(), eq("colorControl"), eq("currentHue"), eq(127)); // 180 degrees -> ~127 + verify(client).setEndpointState(any(), eq("colorControl"), eq("currentSaturation"), eq(254)); // 100% -> 254 + + // Test with brightness 0 (should turn off) + hsb = new HSBType(new DecimalType(180), new PercentType(100), new PercentType(0)); + device.updateState(colorItem, hsb); + verify(client).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(false)); + } + + @Test + void testActivate() { + HSBType hsb = new HSBType(new DecimalType(0), new PercentType(100), new PercentType(100)); + when(colorItem.getStateAs(HSBType.class)).thenReturn(hsb); + colorItem.setState(hsb); + MatterDeviceOptions options = device.activate(); + + Map levelMap = options.clusters.get("levelControl"); + Map colorMap = options.clusters.get("colorControl"); + Map onOffMap = options.clusters.get("onOff"); + + assertNotNull(levelMap); + assertNotNull(colorMap); + assertNotNull(onOffMap); + + assertEquals(254, levelMap.get("currentLevel")); + assertEquals(0, colorMap.get("currentHue")); + assertEquals(254, colorMap.get("currentSaturation")); + assertEquals(true, onOffMap.get("onOff")); + } + + @Test + void testActivateWithOffState() { + HSBType hsb = new HSBType(new DecimalType(0), new PercentType(100), new PercentType(0)); + when(colorItem.getStateAs(HSBType.class)).thenReturn(hsb); + colorItem.setState(hsb); + MatterDeviceOptions options = device.activate(); + + Map levelMap = options.clusters.get("levelControl"); + Map onOffMap = options.clusters.get("onOff"); + assertNotNull(levelMap); + assertNotNull(onOffMap); + assertNotEquals(0, levelMap.get("currentLevel")); + assertEquals(false, onOffMap.get("onOff")); + } + + @AfterEach + void tearDown() { + device.dispose(); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/ContactSensorDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/ContactSensorDeviceTest.java new file mode 100644 index 00000000000..eb91cad68e1 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/ContactSensorDeviceTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.ContactItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; + +/** + * Test class for ContactSensorDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ContactSensorDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + @NonNullByDefault({}) + private ContactItem contactItem; + @NonNullByDefault({}) + private SwitchItem switchItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private ContactSensorDevice contactDevice; + @NonNullByDefault({}) + private ContactSensorDevice switchDevice; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + contactItem = Mockito.spy(new ContactItem("testContact")); + switchItem = Mockito.spy(new SwitchItem("testSwitch")); + contactDevice = new ContactSensorDevice(metadataRegistry, client, contactItem); + switchDevice = new ContactSensorDevice(metadataRegistry, client, switchItem); + } + + @Test + void testDeviceType() { + assertEquals("ContactSensor", contactDevice.deviceType()); + } + + @Test + void testUpdateStateWithContact() { + contactItem.setState(OpenClosedType.OPEN); + contactDevice.updateState(contactItem, OpenClosedType.OPEN); + verify(client).setEndpointState(any(), eq("booleanState"), eq("stateValue"), eq(false)); + + contactItem.setState(OpenClosedType.CLOSED); + contactDevice.updateState(contactItem, OpenClosedType.CLOSED); + verify(client).setEndpointState(any(), eq("booleanState"), eq("stateValue"), eq(true)); + } + + @Test + void testUpdateStateWithSwitch() { + switchItem.setState(OnOffType.ON); + switchDevice.updateState(switchItem, OnOffType.ON); + verify(client).setEndpointState(any(), eq("booleanState"), eq("stateValue"), eq(false)); + + switchItem.setState(OnOffType.OFF); + switchDevice.updateState(switchItem, OnOffType.OFF); + verify(client).setEndpointState(any(), eq("booleanState"), eq("stateValue"), eq(true)); + } + + @Test + void testActivateWithContact() { + contactItem.setState(OpenClosedType.OPEN); + MatterDeviceOptions options = contactDevice.activate(); + + Map booleanStateMap = options.clusters.get("booleanState"); + assertNotNull(booleanStateMap); + assertEquals(false, booleanStateMap.get("stateValue")); + + contactItem.setState(OpenClosedType.CLOSED); + options = contactDevice.activate(); + booleanStateMap = options.clusters.get("booleanState"); + assertNotNull(booleanStateMap); + assertEquals(true, booleanStateMap.get("stateValue")); + } + + @Test + void testActivateWithSwitch() { + switchItem.setState(OnOffType.ON); + MatterDeviceOptions options = switchDevice.activate(); + + Map booleanStateMap = options.clusters.get("booleanState"); + assertNotNull(booleanStateMap); + assertEquals(false, booleanStateMap.get("stateValue")); + + switchItem.setState(OnOffType.OFF); + options = switchDevice.activate(); + booleanStateMap = options.clusters.get("booleanState"); + assertNotNull(booleanStateMap); + assertEquals(true, booleanStateMap.get("stateValue")); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DeviceRegistryTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DeviceRegistryTest.java new file mode 100644 index 00000000000..97348378bc6 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DeviceRegistryTest.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.StringItem; + +/** + * + * @author Dan Cunningham - Initial contribution + */ +@ExtendWith(MockitoExtension.class) +@NonNullByDefault +class DeviceRegistryTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + private GenericItem item = new StringItem("testItem"); + + @Test + void createOnOffLightDevice() { + GenericDevice device = DeviceRegistry.createDevice("OnOffLight", metadataRegistry, client, item); + + assertNotNull(device); + assertTrue(device instanceof OnOffLightDevice); + } + + @Test + void createThermostatDevice() { + GenericDevice device = DeviceRegistry.createDevice("Thermostat", metadataRegistry, client, item); + + assertNotNull(device); + assertTrue(device instanceof ThermostatDevice); + } + + @Test + void createInvalidDeviceType() { + GenericDevice device = DeviceRegistry.createDevice("InvalidDeviceType", metadataRegistry, client, item); + + assertNull(device); + } + + @Test + void createAllDevices() { + String[] deviceTypes = { "OnOffLight", "OnOffPlugInUnit", "DimmableLight", "Thermostat", "WindowCovering", + "DoorLock", "TemperatureSensor", "HumiditySensor", "OccupancySensor", "ContactSensor", "ColorLight" }; + + for (String deviceType : deviceTypes) { + GenericDevice device = DeviceRegistry.createDevice(deviceType, metadataRegistry, client, item); + assertNotNull(device, "Device creation failed for type: " + deviceType); + } + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DimmableLightDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DimmableLightDeviceTest.java new file mode 100644 index 00000000000..16bb4b3f0bc --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DimmableLightDeviceTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.DimmerItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; + +/** + * Test class for DimmableLightDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class DimmableLightDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + @NonNullByDefault({}) + private DimmerItem dimmerItem; + @NonNullByDefault({}) + private SwitchItem switchItem; + @NonNullByDefault({}) + private GroupItem groupItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private DimmableLightDevice dimmerDevice; + @NonNullByDefault({}) + private DimmableLightDevice switchDevice; + @NonNullByDefault({}) + private DimmableLightDevice groupDevice; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + dimmerItem = Mockito.spy(new DimmerItem("testDimmer")); + switchItem = Mockito.spy(new SwitchItem("testSwitch")); + groupItem = Mockito.spy(new GroupItem("testGroup", dimmerItem)); + + dimmerDevice = new DimmableLightDevice(metadataRegistry, client, dimmerItem); + switchDevice = new DimmableLightDevice(metadataRegistry, client, switchItem); + groupDevice = new DimmableLightDevice(metadataRegistry, client, groupItem); + } + + @AfterEach + void tearDown() { + dimmerDevice.dispose(); + switchDevice.dispose(); + groupDevice.dispose(); + } + + @Test + void testDeviceType() { + assertEquals("DimmableLight", dimmerDevice.deviceType()); + } + + @Test + void testHandleMatterEventOnOff() { + dimmerDevice.handleMatterEvent("onOff", "onOff", true); + verify(dimmerItem).send(OnOffType.ON); + + dimmerDevice.handleMatterEvent("onOff", "onOff", false); + verify(dimmerItem).send(OnOffType.OFF); + } + + @Test + void testHandleMatterEventOnOffGroup() { + groupDevice.handleMatterEvent("onOff", "onOff", true); + verify(groupItem).send(OnOffType.ON); + + groupDevice.handleMatterEvent("onOff", "onOff", false); + verify(groupItem).send(OnOffType.OFF); + } + + @Test + void testHandleMatterEventLevel() { + dimmerDevice.handleMatterEvent("onOff", "onOff", true); + verify(dimmerItem).send(OnOffType.ON); + + dimmerDevice.handleMatterEvent("levelControl", "currentLevel", Double.valueOf(254)); + verify(dimmerItem).send(new PercentType(100)); + + dimmerDevice.handleMatterEvent("levelControl", "currentLevel", Double.valueOf(127)); + verify(dimmerItem).send(new PercentType(50)); + } + + @Test + void testHandleMatterEventLevelGroup() { + groupDevice.handleMatterEvent("onOff", "onOff", true); + groupDevice.handleMatterEvent("levelControl", "currentLevel", Double.valueOf(254)); + verify(groupItem).send(new PercentType(100)); + + groupDevice.handleMatterEvent("levelControl", "currentLevel", Double.valueOf(127)); + verify(groupItem).send(new PercentType(50)); + } + + @Test + void testUpdateStateWithPercent() { + dimmerDevice.updateState(dimmerItem, new PercentType(100)); + verify(client).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(true)); + verify(client).setEndpointState(any(), eq("levelControl"), eq("currentLevel"), eq(254)); + + dimmerDevice.updateState(dimmerItem, PercentType.ZERO); + verify(client).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(false)); + } + + @Test + void testActivate() { + dimmerItem.setState(new PercentType(100)); + MatterDeviceOptions options = dimmerDevice.activate(); + + Map levelMap = options.clusters.get("levelControl"); + Map onOffMap = options.clusters.get("onOff"); + + assertNotNull(levelMap); + assertNotNull(onOffMap); + + assertEquals(254, levelMap.get("currentLevel")); + assertEquals(true, onOffMap.get("onOff")); + } + + @Test + void testActivateWithOffState() { + dimmerItem.setState(PercentType.ZERO); + MatterDeviceOptions options = dimmerDevice.activate(); + + Map levelMap = options.clusters.get("levelControl"); + Map onOffMap = options.clusters.get("onOff"); + + assertNotNull(levelMap); + assertNotNull(onOffMap); + + assertNotEquals(0, levelMap.get("currentLevel")); + assertEquals(false, onOffMap.get("onOff")); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DoorLockDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DoorLockDeviceTest.java new file mode 100644 index 00000000000..4cb0c5ebd86 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/DoorLockDeviceTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DoorLockCluster; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; + +/** + * Test class for DoorLockDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class DoorLockDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + @NonNullByDefault({}) + private SwitchItem switchItem; + @NonNullByDefault({}) + private GroupItem groupItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private DoorLockDevice switchDevice; + @NonNullByDefault({}) + private DoorLockDevice groupDevice; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + switchItem = Mockito.spy(new SwitchItem("testSwitch")); + groupItem = Mockito.spy(new GroupItem("testGroup", switchItem)); + + switchDevice = new DoorLockDevice(metadataRegistry, client, switchItem); + groupDevice = new DoorLockDevice(metadataRegistry, client, groupItem); + } + + @Test + void testDeviceType() { + assertEquals("DoorLock", switchDevice.deviceType()); + } + + @Test + void testHandleMatterEventLockState() { + switchDevice.handleMatterEvent("doorLock", "lockState", + Double.valueOf(DoorLockCluster.LockStateEnum.LOCKED.value)); + verify(switchItem).send(OnOffType.ON); + + switchDevice.handleMatterEvent("doorLock", "lockState", + Double.valueOf(DoorLockCluster.LockStateEnum.UNLOCKED.value)); + verify(switchItem).send(OnOffType.OFF); + } + + @Test + void testHandleMatterEventLockStateGroup() { + groupDevice.handleMatterEvent("doorLock", "lockState", + Double.valueOf(DoorLockCluster.LockStateEnum.LOCKED.value)); + verify(groupItem).send(OnOffType.ON); + + groupDevice.handleMatterEvent("doorLock", "lockState", + Double.valueOf(DoorLockCluster.LockStateEnum.UNLOCKED.value)); + verify(groupItem).send(OnOffType.OFF); + } + + @Test + void testUpdateState() { + switchDevice.updateState(switchItem, OnOffType.ON); + verify(client).setEndpointState(any(), eq("doorLock"), eq("lockState"), + eq(DoorLockCluster.LockStateEnum.LOCKED.value)); + + switchDevice.updateState(switchItem, OnOffType.OFF); + verify(client).setEndpointState(any(), eq("doorLock"), eq("lockState"), + eq(DoorLockCluster.LockStateEnum.UNLOCKED.value)); + } + + @Test + void testActivate() { + switchItem.setState(OnOffType.ON); + MatterDeviceOptions options = switchDevice.activate(); + + Map doorLockMap = options.clusters.get("doorLock"); + assertNotNull(doorLockMap); + assertEquals(DoorLockCluster.LockStateEnum.LOCKED.value, doorLockMap.get("lockState")); + } + + @Test + void testActivateWithUnlockedState() { + switchItem.setState(OnOffType.OFF); + MatterDeviceOptions options = switchDevice.activate(); + + Map doorLockMap = options.clusters.get("doorLock"); + assertNotNull(doorLockMap); + assertEquals(DoorLockCluster.LockStateEnum.UNLOCKED.value, doorLockMap.get("lockState")); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/GenericDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/GenericDeviceTest.java new file mode 100644 index 00000000000..fc61b7e5c48 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/GenericDeviceTest.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MetaDataMapping; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.items.GenericItem; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; + +/** + * Test class for GenericDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class GenericDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + @NonNullByDefault({}) + private NumberItem numberItem; + @NonNullByDefault({}) + private TestGenericDevice device; + + @SuppressWarnings("null") + private static class TestGenericDevice extends GenericDevice { + public TestGenericDevice(MetadataRegistry metadataRegistry, MatterBridgeClient client, + GenericItem primaryItem) { + super(metadataRegistry, client, primaryItem); + } + + @Override + public String deviceType() { + return "TestDevice"; + } + + @Override + public MatterDeviceOptions activate() { + return new MatterDeviceOptions(Map.of(), "test"); + } + + @Override + public void dispose() { + } + + @Override + public void updateState(org.openhab.core.items.Item item, org.openhab.core.types.State state) { + } + + @Override + public void handleMatterEvent(String clusterName, String attributeName, Object data) { + } + } + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + Map config = Map.of("label", "Test Label", "fixedLabels", "ON=1, OFF=0", "cluster.attribute", + "value"); + Metadata metadata = new Metadata(key, "attr1,attr2", config); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + when(client.addEndpoint(any(), any(), any(), any(), any(), any(), any())) + .thenReturn(CompletableFuture.completedFuture("success")); + + numberItem = new NumberItem("testNumber"); + device = new TestGenericDevice(metadataRegistry, client, numberItem); + } + + @Test + void testLevelToPercent() { + assertEquals(0, ValueUtils.levelToPercent(0).intValue()); + assertEquals(50, ValueUtils.levelToPercent(127).intValue()); + assertEquals(100, ValueUtils.levelToPercent(254).intValue()); + } + + @Test + void testPercentToLevel() { + assertEquals(0, ValueUtils.percentToLevel(new PercentType(0))); + assertEquals(127, ValueUtils.percentToLevel(new PercentType(50))); + assertEquals(254, ValueUtils.percentToLevel(new PercentType(100))); + } + + @Test + void testTemperatureToValue() { + // Test Celsius values + assertEquals(2000, ValueUtils.temperatureToValue(new QuantityType(20.0, SIUnits.CELSIUS))); + assertEquals(0, ValueUtils.temperatureToValue(new QuantityType(0.0, SIUnits.CELSIUS))); + assertEquals(-1000, ValueUtils.temperatureToValue(new QuantityType(-10.0, SIUnits.CELSIUS))); + + // Test Fahrenheit values + assertEquals(0, ValueUtils.temperatureToValue(new QuantityType(32.0, ImperialUnits.FAHRENHEIT))); + assertEquals(2000, + ValueUtils.temperatureToValue(new QuantityType(68.0, ImperialUnits.FAHRENHEIT))); + + // Test DecimalType (assumed Celsius) + assertEquals(2000, ValueUtils.temperatureToValue(new DecimalType(20.0))); + assertEquals(-1000, ValueUtils.temperatureToValue(new DecimalType(-10.0))); + } + + @SuppressWarnings({ "null" }) + @Test + void testValueToTemperature() { + assertEquals(20.0, ValueUtils.valueToTemperature(2000).toUnit(SIUnits.CELSIUS).doubleValue(), 0.01); + assertEquals(0.0, ValueUtils.valueToTemperature(0).toUnit(SIUnits.CELSIUS).doubleValue(), 0.01); + assertEquals(-10.0, ValueUtils.valueToTemperature(-1000).toUnit(SIUnits.CELSIUS).doubleValue(), 0.01); + } + + @Test + void testMetaDataMapping() { + MetaDataMapping mapping = device.metaDataMapping(numberItem); + assertEquals("Test Label", mapping.label); + assertEquals(2, mapping.attributes.size()); + assertTrue(mapping.attributes.contains("attr1")); + assertTrue(mapping.attributes.contains("attr2")); + assertEquals("value", mapping.getAttributeOptions().get("cluster.attribute")); + } + + @Test + void testRegisterDevice() { + device.registerDevice(); + verify(client).addEndpoint(eq("TestDevice"), eq("testNumber"), eq("test"), eq("testNumber"), eq("Type Number"), + any(), any()); + } + + @Test + void testMapClusterAttributes() { + Map attributes = Map.of("cluster1.attr1", "value1", "cluster1.attr2", "value2", + "cluster2.attr1", "value3"); + + Map> result = device.mapClusterAttributes(attributes); + assertNotNull(result); + Map cluster1 = result.get("cluster1"); + Map cluster2 = result.get("cluster2"); + assertNotNull(cluster1); + assertNotNull(cluster2); + assertEquals("value1", cluster1.get("attr1")); + assertEquals("value2", cluster1.get("attr2")); + assertEquals("value3", cluster2.get("attr1")); + } + + @Test + void testMapClusterAttributesInvalidFormat() { + Map attributes = Map.of("invalidFormat", "value"); + assertThrows(IllegalArgumentException.class, () -> device.mapClusterAttributes(attributes)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/HumiditySensorDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/HumiditySensorDeviceTest.java new file mode 100644 index 00000000000..755faf16aac --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/HumiditySensorDeviceTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import javax.measure.quantity.Dimensionless; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; + +/** + * Test class for HumiditySensorDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class HumiditySensorDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + + @NonNullByDefault({}) + private NumberItem numberItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private HumiditySensorDevice device; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + numberItem = Mockito.spy(new NumberItem("testHumidity")); + device = new HumiditySensorDevice(metadataRegistry, client, numberItem); + } + + @Test + void testDeviceType() { + assertEquals("HumiditySensor", device.deviceType()); + } + + @Test + void testUpdateStateWithDecimalType() { + device.updateState(numberItem, new DecimalType(50.0)); + verify(client).setEndpointState(any(), eq("relativeHumidityMeasurement"), eq("measuredValue"), eq(5000)); + + device.updateState(numberItem, new DecimalType(0.0)); + verify(client).setEndpointState(any(), eq("relativeHumidityMeasurement"), eq("measuredValue"), eq(0)); + + device.updateState(numberItem, new DecimalType(100.0)); + verify(client).setEndpointState(any(), eq("relativeHumidityMeasurement"), eq("measuredValue"), eq(10000)); + } + + @Test + void testUpdateStateWithQuantityType() { + device.updateState(numberItem, new QuantityType(50.0, Units.PERCENT)); + verify(client).setEndpointState(any(), eq("relativeHumidityMeasurement"), eq("measuredValue"), eq(5000)); + + device.updateState(numberItem, new QuantityType(0.0, Units.PERCENT)); + verify(client).setEndpointState(any(), eq("relativeHumidityMeasurement"), eq("measuredValue"), eq(0)); + + device.updateState(numberItem, new QuantityType(100.0, Units.PERCENT)); + verify(client).setEndpointState(any(), eq("relativeHumidityMeasurement"), eq("measuredValue"), eq(10000)); + } + + @Test + void testActivate() { + numberItem.setState(new DecimalType(50.0)); + MatterDeviceOptions options = device.activate(); + + Map humidityMap = options.clusters.get("relativeHumidityMeasurement"); + assertNotNull(humidityMap); + assertEquals(5000, humidityMap.get("measuredValue")); + } + + @Test + void testActivateWithZeroState() { + numberItem.setState(new DecimalType(0)); + MatterDeviceOptions options = device.activate(); + + Map humidityMap = options.clusters.get("relativeHumidityMeasurement"); + assertNotNull(humidityMap); + assertEquals(0, humidityMap.get("measuredValue")); + } + + @Test + void testActivateWithMaxState() { + numberItem.setState(new DecimalType(100)); + MatterDeviceOptions options = device.activate(); + + Map humidityMap = options.clusters.get("relativeHumidityMeasurement"); + assertNotNull(humidityMap); + assertEquals(10000, humidityMap.get("measuredValue")); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/OccupancySensorDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/OccupancySensorDeviceTest.java new file mode 100644 index 00000000000..4285292c138 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/OccupancySensorDeviceTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.ContactItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.OpenClosedType; + +import com.google.gson.JsonObject; + +/** + * Test class for OccupancySensorDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class OccupancySensorDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + @NonNullByDefault({}) + private SwitchItem switchItem; + @NonNullByDefault({}) + private ContactItem contactItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private OccupancySensorDevice switchDevice; + @NonNullByDefault({}) + private OccupancySensorDevice contactDevice; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + switchItem = Mockito.spy(new SwitchItem("testSwitch")); + contactItem = Mockito.spy(new ContactItem("testContact")); + + switchDevice = new OccupancySensorDevice(metadataRegistry, client, switchItem); + contactDevice = new OccupancySensorDevice(metadataRegistry, client, contactItem); + } + + @Test + void testDeviceType() { + assertEquals("OccupancySensor", switchDevice.deviceType()); + } + + @Test + void testUpdateStateWithSwitch() { + JsonObject occupiedJson = new JsonObject(); + occupiedJson.addProperty("occupied", true); + JsonObject unoccupiedJson = new JsonObject(); + unoccupiedJson.addProperty("occupied", false); + + switchItem.setState(OnOffType.ON); + switchDevice.updateState(switchItem, OnOffType.ON); + verify(client).setEndpointState(any(), eq("occupancySensing"), eq("occupancy"), eq(occupiedJson)); + + switchItem.setState(OnOffType.OFF); + switchDevice.updateState(switchItem, OnOffType.OFF); + verify(client).setEndpointState(any(), eq("occupancySensing"), eq("occupancy"), eq(unoccupiedJson)); + } + + @Test + void testUpdateStateWithContact() { + JsonObject occupiedJson = new JsonObject(); + occupiedJson.addProperty("occupied", true); + JsonObject unoccupiedJson = new JsonObject(); + unoccupiedJson.addProperty("occupied", false); + + contactItem.setState(OpenClosedType.OPEN); + contactDevice.updateState(contactItem, OpenClosedType.OPEN); + verify(client).setEndpointState(any(), eq("occupancySensing"), eq("occupancy"), eq(occupiedJson)); + + contactItem.setState(OpenClosedType.CLOSED); + contactDevice.updateState(contactItem, OpenClosedType.CLOSED); + verify(client).setEndpointState(any(), eq("occupancySensing"), eq("occupancy"), eq(unoccupiedJson)); + } + + @Test + void testActivateWithSwitch() { + JsonObject occupiedJson = new JsonObject(); + occupiedJson.addProperty("occupied", true); + + switchItem.setState(OnOffType.ON); + MatterDeviceOptions options = switchDevice.activate(); + + Map occupancyMap = options.clusters.get("occupancySensing"); + assertNotNull(occupancyMap); + Object occupancy = occupancyMap.get("occupancy"); + assertNotNull(occupancy); + assertEquals(occupiedJson.toString(), occupancy.toString()); + } + + @Test + void testActivateWithContact() { + JsonObject occupiedJson = new JsonObject(); + occupiedJson.addProperty("occupied", true); + + contactItem.setState(OpenClosedType.OPEN); + MatterDeviceOptions options = contactDevice.activate(); + + Map occupancyMap = options.clusters.get("occupancySensing"); + assertNotNull(occupancyMap); + Object occupancy = occupancyMap.get("occupancy"); + assertNotNull(occupancy); + assertEquals(occupiedJson.toString(), occupancy.toString()); + } + + @Test + void testActivateWithUnoccupiedState() { + JsonObject unoccupiedJson = new JsonObject(); + unoccupiedJson.addProperty("occupied", false); + + switchItem.setState(OnOffType.OFF); + MatterDeviceOptions options = switchDevice.activate(); + + Map occupancyMap = options.clusters.get("occupancySensing"); + assertNotNull(occupancyMap); + Object occupancy = occupancyMap.get("occupancy"); + assertNotNull(occupancy); + assertEquals(unoccupiedJson.toString(), occupancy.toString()); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/OnOffLightDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/OnOffLightDeviceTest.java new file mode 100644 index 00000000000..8740a84515a --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/OnOffLightDeviceTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.GroupItem; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; + +/** + * Test class for OnOffLightDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class OnOffLightDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + @NonNullByDefault({}) + private SwitchItem switchItem; + @NonNullByDefault({}) + private GroupItem groupItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private OnOffLightDevice device; + @NonNullByDefault({}) + private OnOffLightDevice groupDevice; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + switchItem = Mockito.spy(new SwitchItem("test")); + groupItem = Mockito.spy(new GroupItem("testGroup", switchItem)); + + device = new OnOffLightDevice(metadataRegistry, client, switchItem); + groupDevice = new OnOffLightDevice(metadataRegistry, client, groupItem); + } + + @Test + void testDeviceType() { + assertEquals("OnOffLight", device.deviceType()); + } + + @Test + void testHandleMatterEventOnOff() { + device.handleMatterEvent("onOff", "onOff", true); + verify(switchItem).send(OnOffType.ON); + + device.handleMatterEvent("onOff", "onOff", false); + verify(switchItem).send(OnOffType.OFF); + } + + @Test + void testHandleMatterEventOnOffGroup() { + groupDevice.handleMatterEvent("onOff", "onOff", true); + verify(groupItem).send(OnOffType.ON); + + groupDevice.handleMatterEvent("onOff", "onOff", false); + verify(groupItem).send(OnOffType.OFF); + } + + @Test + void testUpdateStateWithOnOff() { + device.updateState(switchItem, OnOffType.ON); + verify(client, times(1)).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(true)); + + device.updateState(switchItem, OnOffType.OFF); + verify(client, times(1)).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(false)); + } + + @Test + void testUpdateStateWithPercent() { + device.updateState(switchItem, new PercentType(100)); + verify(client, times(1)).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(true)); + + device.updateState(switchItem, PercentType.ZERO); + verify(client, times(1)).setEndpointState(any(), eq("onOff"), eq("onOff"), eq(false)); + } + + @Test + void testActivate() { + switchItem.setState(OnOffType.ON); + MatterDeviceOptions options = device.activate(); + + Map onOffMap = options.clusters.get("onOff"); + assertNotNull(onOffMap); + assertEquals(1, options.clusters.size()); + assertEquals(true, onOffMap.get("onOff")); + } + + @Test + void testActivateWithOffState() { + switchItem.setState(OnOffType.OFF); + MatterDeviceOptions options = device.activate(); + + Map onOffMap = options.clusters.get("onOff"); + assertNotNull(onOffMap); + assertEquals(1, options.clusters.size()); + assertEquals(false, onOffMap.get("onOff")); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/TemperatureSensorDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/TemperatureSensorDeviceTest.java new file mode 100644 index 00000000000..cc3067e2bc6 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/TemperatureSensorDeviceTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Map; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.NumberItem; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; + +/** + * Test class for TemperatureSensorDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class TemperatureSensorDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + @NonNullByDefault({}) + private NumberItem numberItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private TemperatureSensorDevice device; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + metadata = new Metadata(key, "test", Map.of()); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + + numberItem = Mockito.spy(new NumberItem("testTemperature")); + device = new TemperatureSensorDevice(metadataRegistry, client, numberItem); + } + + @Test + void testDeviceType() { + assertEquals("TemperatureSensor", device.deviceType()); + } + + @Test + void testUpdateStateWithDecimalType() { + device.updateState(numberItem, new DecimalType(20.0)); + verify(client).setEndpointState(any(), eq("temperatureMeasurement"), eq("measuredValue"), eq(2000)); + + device.updateState(numberItem, new DecimalType(0.0)); + verify(client).setEndpointState(any(), eq("temperatureMeasurement"), eq("measuredValue"), eq(0)); + + device.updateState(numberItem, new DecimalType(-10.0)); + verify(client).setEndpointState(any(), eq("temperatureMeasurement"), eq("measuredValue"), eq(-1000)); + } + + @Test + void testUpdateStateWithQuantityType() { + device.updateState(numberItem, new QuantityType(20.0, SIUnits.CELSIUS)); + verify(client).setEndpointState(any(), eq("temperatureMeasurement"), eq("measuredValue"), eq(2000)); + + device.updateState(numberItem, new QuantityType(0.0, SIUnits.CELSIUS)); + verify(client).setEndpointState(any(), eq("temperatureMeasurement"), eq("measuredValue"), eq(0)); + + device.updateState(numberItem, new QuantityType(-10.0, SIUnits.CELSIUS)); + verify(client).setEndpointState(any(), eq("temperatureMeasurement"), eq("measuredValue"), eq(-1000)); + } + + @Test + void testActivate() { + numberItem.setState(new DecimalType(20.0)); + MatterDeviceOptions options = device.activate(); + + Map temperatureMap = options.clusters.get("temperatureMeasurement"); + assertNotNull(temperatureMap); + assertEquals(2000, temperatureMap.get("measuredValue")); + } + + @Test + void testActivateWithZeroState() { + numberItem.setState(new DecimalType(0)); + MatterDeviceOptions options = device.activate(); + + Map temperatureMap = options.clusters.get("temperatureMeasurement"); + assertNotNull(temperatureMap); + assertEquals(0, temperatureMap.get("measuredValue")); + } + + @Test + void testActivateWithNegativeState() { + numberItem.setState(new DecimalType(-10)); + MatterDeviceOptions options = device.activate(); + + Map temperatureMap = options.clusters.get("temperatureMeasurement"); + assertNotNull(temperatureMap); + assertEquals(-1000, temperatureMap.get("measuredValue")); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/WindowCoveringDeviceTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/WindowCoveringDeviceTest.java new file mode 100644 index 00000000000..c9d407d6354 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/bridge/devices/WindowCoveringDeviceTest.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.bridge.devices; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.AbstractMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.bridge.devices.GenericDevice.MatterDeviceOptions; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WindowCoveringCluster; +import org.openhab.core.items.Metadata; +import org.openhab.core.items.MetadataKey; +import org.openhab.core.items.MetadataRegistry; +import org.openhab.core.library.items.DimmerItem; +import org.openhab.core.library.items.RollershutterItem; +import org.openhab.core.library.items.StringItem; +import org.openhab.core.library.items.SwitchItem; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.library.types.UpDownType; + +/** + * Test class for WindowCoveringDevice + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class WindowCoveringDeviceTest { + + @Mock + @NonNullByDefault({}) + private MetadataRegistry metadataRegistry; + @Mock + @NonNullByDefault({}) + private MatterBridgeClient client; + @NonNullByDefault({}) + private RollershutterItem rollershutterItem; + @NonNullByDefault({}) + private DimmerItem dimmerItem; + @NonNullByDefault({}) + private SwitchItem switchItem; + @NonNullByDefault({}) + private StringItem stringItem; + @NonNullByDefault({}) + private Metadata metadata; + @NonNullByDefault({}) + private WindowCoveringDevice device; + @NonNullByDefault({}) + private WindowCoveringDevice dimmerDevice; + @NonNullByDefault({}) + private WindowCoveringDevice switchDevice; + @NonNullByDefault({}) + private WindowCoveringDevice stringDevice; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + MetadataKey key = new MetadataKey("matter", "test"); + Map config = Map.of("OPEN", "UP", "CLOSED", "DOWN"); + metadata = new Metadata(key, "test", config); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(metadata); + when(client.setEndpointState(any(), any(), any(), any())).thenReturn(CompletableFuture.completedFuture(null)); + + rollershutterItem = Mockito.spy(new RollershutterItem("testRoller")); + dimmerItem = Mockito.spy(new DimmerItem("testDimmer")); + switchItem = Mockito.spy(new SwitchItem("testSwitch")); + stringItem = Mockito.spy(new StringItem("testString")); + + device = new WindowCoveringDevice(metadataRegistry, client, rollershutterItem); + dimmerDevice = new WindowCoveringDevice(metadataRegistry, client, dimmerItem); + switchDevice = new WindowCoveringDevice(metadataRegistry, client, switchItem); + stringDevice = new WindowCoveringDevice(metadataRegistry, client, stringItem); + } + + @Test + void testDeviceType() { + assertEquals("WindowCovering", device.deviceType()); + } + + @Test + void testHandleMatterEventPosition() { + device.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 5000.0); + verify(rollershutterItem).send(new PercentType(50)); + } + + @Test + void testHandleMatterEventStop() { + AbstractMap stoppedStatus = new LinkedHashMap(); + stoppedStatus.put("global", WindowCoveringCluster.MovementStatus.STOPPED.getValue()); + + device.handleMatterEvent("windowCovering", "operationalStatus", stoppedStatus); + verify(rollershutterItem).send(StopMoveType.STOP); + } + + @Test + void testUpdateState() throws InterruptedException { + rollershutterItem.setState(new PercentType(50)); + device.updateState(rollershutterItem, rollershutterItem.getState()); + Thread.sleep(1100); // Wait for timer + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(5000)); + } + + @Test + void testActivate() { + rollershutterItem.setState(new PercentType(50)); + MatterDeviceOptions options = device.activate(); + + Map coveringMap = options.clusters.get("windowCovering"); + assertNotNull(coveringMap); + assertEquals(5000, coveringMap.get("currentPositionLiftPercent100ths")); + } + + @Test + void testHandleMatterEventWithDimmer() { + // Test 50% position + dimmerDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 5000.0); + verify(dimmerItem).send(new PercentType(50)); + + // Test fully open + dimmerDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 0.0); + verify(dimmerItem).send(new PercentType(0)); + + // Test fully closed + dimmerDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 10000.0); + verify(dimmerItem).send(new PercentType(100)); + } + + @Test + void testHandleMatterEventWithRollershutter() { + // Test fully closed + device.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 10000.0); + verify(rollershutterItem).send(UpDownType.DOWN); + + // Test fully open + device.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 0.0); + verify(rollershutterItem).send(UpDownType.UP); + + // Test 50% position + device.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 5000.0); + verify(rollershutterItem).send(new PercentType(50)); + } + + @Test + void testHandleMatterEventWithSwitch() { + // Test fully closed + switchDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 10000.0); + verify(switchItem).send(OnOffType.ON); + + // Test fully open + switchDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 0.0); + verify(switchItem).send(OnOffType.OFF); + } + + @Test + void testHandleMatterEventWithString() { + // Test fully open + stringDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 0.0); + verify(stringItem).send(new StringType("UP")); + + // Test fully closed + stringDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 10000.0); + verify(stringItem).send(new StringType("DOWN")); + } + + @Test + void testUpdateStateWithDimmer() throws InterruptedException { + dimmerItem.setState(new PercentType(50)); + dimmerDevice.updateState(dimmerItem, dimmerItem.getState()); + Thread.sleep(1100); // Wait for timer + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(5000)); + } + + @Test + void testUpdateStateWithSwitch() throws InterruptedException { + switchItem.setState(OnOffType.ON); + switchDevice.updateState(switchItem, switchItem.getState()); + Thread.sleep(1100); // Wait for timer + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(10000)); + + switchItem.setState(OnOffType.OFF); + switchDevice.updateState(switchItem, switchItem.getState()); + Thread.sleep(1100); // Wait for timer + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(0)); + } + + @Test + void testUpdateStateWithString() throws InterruptedException { + stringItem.setState(new StringType("UP")); + stringDevice.updateState(stringItem, stringItem.getState()); + Thread.sleep(1100); // Wait for timer + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(0)); + + stringItem.setState(new StringType("DOWN")); + stringDevice.updateState(stringItem, stringItem.getState()); + Thread.sleep(1100); // Wait for timer + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(10000)); + } + + @Test + void testSwitchItemWithInvertedLogic() { + // Create metadata with invert=true configuration + MetadataKey key = new MetadataKey("matter", "testSwitch"); + Map config = Map.of("invert", true); + Metadata invertedMetadata = new Metadata(key, "test", config); + when(metadataRegistry.get(any(MetadataKey.class))).thenReturn(invertedMetadata); + + // Create new device instance with inverted switch + @SuppressWarnings("null") + SwitchItem invertedSwitchItem = Mockito.spy(new SwitchItem("testInvertedSwitch")); + WindowCoveringDevice invertedSwitchDevice = new WindowCoveringDevice(metadataRegistry, client, + invertedSwitchItem); + + // Test fully closed (100%) - should result in OFF for inverted switch + invertedSwitchDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 10000.0); + verify(invertedSwitchItem).send(OnOffType.OFF); + + // Test fully open (0%) - should result in ON for inverted switch + invertedSwitchDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 0.0); + verify(invertedSwitchItem).send(OnOffType.ON); + + // Test state updates from switch to position + invertedSwitchItem.setState(OnOffType.ON); + invertedSwitchDevice.updateState(invertedSwitchItem, invertedSwitchItem.getState()); + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(0)); + + invertedSwitchItem.setState(OnOffType.OFF); + invertedSwitchDevice.updateState(invertedSwitchItem, invertedSwitchItem.getState()); + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(10000)); + + invertedSwitchDevice.dispose(); + } + + @Test + void testSwitchItemWithNormalLogic() { + // Test fully closed (100%) - should result in ON for normal switch + switchDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 10000.0); + verify(switchItem).send(OnOffType.ON); + + // Test fully open (0%) - should result in OFF for normal switch + switchDevice.handleMatterEvent("windowCovering", "targetPositionLiftPercent100ths", 0.0); + verify(switchItem).send(OnOffType.OFF); + + // Test state updates from switch to position + switchItem.setState(OnOffType.ON); + switchDevice.updateState(switchItem, switchItem.getState()); + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(10000)); + + switchItem.setState(OnOffType.OFF); + switchDevice.updateState(switchItem, switchItem.getState()); + verify(client).setEndpointState(any(), eq("windowCovering"), eq("currentPositionLiftPercent100ths"), eq(0)); + } + + @AfterEach + void tearDown() { + device.dispose(); + dimmerDevice.dispose(); + switchDevice.dispose(); + stringDevice.dispose(); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/client/MatterWebsocketClientTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/client/MatterWebsocketClientTest.java new file mode 100644 index 00000000000..429f570fef7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/client/MatterWebsocketClientTest.java @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.client; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openhab.binding.matter.internal.client.dto.Endpoint; +import org.openhab.binding.matter.internal.client.dto.Node; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DescriptorCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OccupancySensingCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Message; + +import com.google.gson.JsonObject; + +/** + * Test class for the MatterWebsocketClient class. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class MatterWebsocketClientTest { + + @NonNullByDefault({}) + private MatterWebsocketClient client; + + @BeforeEach + void setUp() { + client = new MatterWebsocketClient(); + } + + @Test + void testDeserializeNode() { + String json = """ + { + "id": "1234567890", + "rootEndpoint": { + "number": 0, + "clusters": { + "Descriptor": { + "id": 29, + "name": "Descriptor" + }, + "BasicInformation": { + "id": 40, + "name": "BasicInformation" + } + }, + "children": [ + { + "number": 1, + "clusters": { + "Descriptor": { + "id": 29, + "name": "Descriptor" + } + } + } + ] + } + } + """; + Node node = client.getGson().fromJson(json, Node.class); + assertNotNull(node); + assertEquals(new BigInteger("1234567890"), node.id); + assertEquals(1, node.rootEndpoint.children.size()); + Endpoint endpoint = node.rootEndpoint.children.get(0); + assertNotNull(endpoint); + assertEquals(1, endpoint.clusters.size()); + } + + @Test + void testDeserializeAttributeChangedMessage() { + String json = """ + { + "path": { + "clusterId": 1, + "attributeName": "testAttribute" + }, + "version": 1, + "value": "testValue" + } + """; + AttributeChangedMessage message = client.getGson().fromJson(json, AttributeChangedMessage.class); + assertNotNull(message); + assertEquals("testAttribute", message.path.attributeName); + assertEquals(1, message.version); + assertEquals("testValue", message.value); + } + + @Test + void testDeserializeEventTriggeredMessage() { + String json = """ + { + "path": { + "clusterId": 1, + "eventName": "testEvent" + }, + "events": [] + } + """; + EventTriggeredMessage message = client.getGson().fromJson(json, EventTriggeredMessage.class); + assertNotNull(message); + assertEquals("testEvent", message.path.eventName); + assertEquals(0, message.events.length); + } + + @Test + void testDeserializeGenericMessage() { + String json = """ + { + "type": "response", + "message": { + "type": "resultSuccess", + "id": "1", + "result": {} + } + } + """; + Message message = client.getGson().fromJson(json, Message.class); + assertNotNull(message); + assertEquals("response", message.type); + } + + @Test + void testDeserializeBasicCluster() { + String json = """ + { + "type": "response", + "message": { + "type": "resultSuccess", + "id": "example-id", + "result": { + "id": "8507467286360628650", + "rootEndpoint": { + "number": 0, + "clusters": { + "Descriptor": { + "id": 29, + "name": "Descriptor", + "deviceTypeList": [ + { + "deviceType": 22, + "revision": 1 + } + ] + } + } + } + } + } + } + """; + Message message = client.getGson().fromJson(json, Message.class); + assertNotNull(message); + JsonObject descriptorJson = message.message.getAsJsonObject("result").getAsJsonObject("rootEndpoint") + .getAsJsonObject("clusters").getAsJsonObject("Descriptor"); + DescriptorCluster descriptorCluster = client.getGson().fromJson(descriptorJson, DescriptorCluster.class); + assertNotNull(descriptorCluster); + assertEquals(29, DescriptorCluster.CLUSTER_ID); + assertEquals("Descriptor", DescriptorCluster.CLUSTER_NAME); + assertNotNull(descriptorCluster.deviceTypeList); + assertEquals(1, descriptorCluster.deviceTypeList.size()); + assertEquals(22, descriptorCluster.deviceTypeList.get(0).deviceType); + assertEquals(1, descriptorCluster.deviceTypeList.get(0).revision); + } + + @Test + void testDeserializeOnOffCluster() { + String json = """ + { + "type": "response", + "message": { + "type": "resultSuccess", + "id": "example-id", + "result": { + "id": "4596455042137293483", + "endpoints": { + "1": { + "number": 1, + "clusters": { + "OnOff": { + "id": 6, + "name": "OnOff", + "onOff": false, + "clusterRevision": 4, + "featureMap": { + "lighting": true, + "deadFrontBehavior": false, + "offOnly": false + } + } + } + } + } + } + } + } + """; + Message message = client.getGson().fromJson(json, Message.class); + assertNotNull(message); + JsonObject onOffClusterJson = message.message.getAsJsonObject("result").getAsJsonObject("endpoints") + .getAsJsonObject("1").getAsJsonObject("clusters").getAsJsonObject("OnOff"); + OnOffCluster onOffCluster = client.getGson().fromJson(onOffClusterJson, OnOffCluster.class); + assertNotNull(onOffCluster); + assertEquals(6, onOffCluster.id); + assertEquals("OnOff", onOffCluster.name); + assertEquals(false, onOffCluster.onOff); + assertEquals(4, onOffCluster.clusterRevision); + assertNotNull(onOffCluster.featureMap); + assertEquals(true, onOffCluster.featureMap.lighting); + } + + @Test + void testDeserializeLevelControlCluster() { + String json = """ + { + "type": "response", + "message": { + "type": "resultSuccess", + "id": "example-id", + "result": { + "id": "4596455042137293483", + "endpoints": { + "1": { + "number": 1, + "clusters": { + "LevelControl": { + "id": 8, + "name": "LevelControl", + "currentLevel": 254, + "maxLevel": 254, + "options": { + "executeIfOff": false, + "coupleColorTempToLevel": false + }, + "onOffTransitionTime": 5, + "onLevel": 254, + "defaultMoveRate": 50, + "clusterRevision": 5, + "featureMap": { + "onOff": true, + "lighting": true, + "frequency": false + } + } + } + } + } + } + } + } + """; + Message message = client.getGson().fromJson(json, Message.class); + assertNotNull(message); + JsonObject levelControlClusterJson = message.message.getAsJsonObject("result").getAsJsonObject("endpoints") + .getAsJsonObject("1").getAsJsonObject("clusters").getAsJsonObject("LevelControl"); + + LevelControlCluster levelControlCluster = client.getGson().fromJson(levelControlClusterJson, + LevelControlCluster.class); + assertNotNull(levelControlCluster); + assertEquals(8, levelControlCluster.id); + assertEquals("LevelControl", levelControlCluster.name); + assertEquals(254, levelControlCluster.currentLevel); + assertEquals(5, levelControlCluster.onOffTransitionTime); + assertNotNull(levelControlCluster.featureMap); + assertEquals(true, levelControlCluster.featureMap.onOff); + } + + @Test + void testDeserializeOccupancyAttributeChangedMessage() { + String json = """ + { + "path": { + "nodeId": "4643639431978709653", + "endpointId": 6, + "clusterId": 1030, + "attributeId": 0, + "attributeName": "occupancy" + }, + "version": 2038225370, + "value": { + "occupied": true + } + } + """; + AttributeChangedMessage message = client.getGson().fromJson(json, AttributeChangedMessage.class); + assertNotNull(message); + assertEquals("occupancy", message.path.attributeName); + assertEquals(1030, message.path.clusterId); + assertEquals(0, message.path.attributeId); + + OccupancySensingCluster.OccupancyBitmap occupancyBitmap = (OccupancySensingCluster.OccupancyBitmap) message.value; + assertNotNull(occupancyBitmap); + assertEquals(true, occupancyBitmap.occupied); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/BaseMatterConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/BaseMatterConverterTest.java new file mode 100644 index 00000000000..5c308ff4b34 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/BaseMatterConverterTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.openhab.binding.matter.internal.MatterChannelTypeProvider; +import org.openhab.binding.matter.internal.MatterConfigDescriptionProvider; +import org.openhab.binding.matter.internal.MatterStateDescriptionOptionProvider; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; + +/** + * Base class for Matter converter tests + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public abstract class BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + protected MatterBridgeClient mockBridgeClient; + @Mock + @NonNullByDefault({}) + protected BaseThingHandlerFactory mockThingHandlerFactory; + @Mock + @NonNullByDefault({}) + protected MatterStateDescriptionOptionProvider mockStateDescriptionProvider; + @Mock + @NonNullByDefault({}) + protected MatterChannelTypeProvider mockChannelTypeProvider; + @Mock + @NonNullByDefault({}) + protected MatterConfigDescriptionProvider mockConfigDescriptionProvider; + @NonNullByDefault({}) + protected TestMatterBaseThingHandler mockHandler; + @NonNullByDefault({}) + protected TemperatureMeasurementConverter converter; + @Mock + @NonNullByDefault({}) + protected TranslationService mockTranslationService; + + @SuppressWarnings("null") + void setUp() { + MockitoAnnotations.openMocks(this); + mockHandler = Mockito.spy( + new TestMatterBaseThingHandler(mockBridgeClient, mockThingHandlerFactory, mockStateDescriptionProvider, + mockChannelTypeProvider, mockConfigDescriptionProvider, mockTranslationService)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/BooleanStateConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/BooleanStateConverterTest.java new file mode 100644 index 00000000000..509e006599a --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/BooleanStateConverterTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.BooleanStateCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for BooleanStateConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class BooleanStateConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private BooleanStateCluster mockCluster; + @NonNullByDefault({}) + private BooleanStateConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + converter = new BooleanStateConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID thingUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(thingUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#booleanstate-statevalue", channel.getUID().toString()); + assertEquals("Switch", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithBooleanValue() throws Exception { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "stateValue"; + message.value = true; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("booleanstate-statevalue"), eq(OnOffType.ON)); + } + + @Test + void testOnEventWithBooleanValueFalse() throws Exception { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "stateValue"; + message.value = false; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("booleanstate-statevalue"), eq(OnOffType.OFF)); + } + + @Test + void testInitState() throws Exception { + mockCluster.stateValue = true; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("booleanstate-statevalue"), eq(OnOffType.ON)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ColorControlConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ColorControlConverterTest.java new file mode 100644 index 00000000000..948a629c87e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ColorControlConverterTest.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ColorControlCluster.ColorModeEnum; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.HSBType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for ColorControlConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +@ExtendWith(MockitoExtension.class) +class ColorControlConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private ColorControlCluster mockColorCluster; + @Mock + @NonNullByDefault({}) + private LevelControlCluster mockLevelCluster; + @Mock + @NonNullByDefault({}) + private ColorControlConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockColorCluster.featureMap = new ColorControlCluster.FeatureMap(true, false, false, false, true); + mockColorCluster.colorTempPhysicalMinMireds = 153; + mockColorCluster.colorTempPhysicalMaxMireds = 500; + mockLevelCluster.currentLevel = 254; + + converter = new ColorControlConverter(mockColorCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + + assertEquals(3, channels.size()); + for (Channel channel : channels.keySet()) { + String channelId = channel.getUID().getIdWithoutGroup(); + switch (channelId) { + case "colorcontrol-color": + assertEquals("Color", channel.getAcceptedItemType()); + break; + case "colorcontrol-colortemperature": + assertEquals("Dimmer", channel.getAcceptedItemType()); + break; + case "colorcontrol-colortemperature-abs": + assertEquals("Number:Temperature", channel.getAcceptedItemType()); + break; + } + } + } + + @Test + void testOnEventWithHueSaturation() throws InterruptedException { + converter.supportsHue = true; + + AttributeChangedMessage levelMsg = new AttributeChangedMessage(); + levelMsg.path = new Path(); + levelMsg.path.attributeName = "currentLevel"; + levelMsg.value = 254; // 100% + converter.onEvent(levelMsg); + + AttributeChangedMessage modeMsg = new AttributeChangedMessage(); + modeMsg.path = new Path(); + modeMsg.path.attributeName = "colorMode"; + modeMsg.value = ColorModeEnum.CURRENT_HUE_AND_CURRENT_SATURATION; + converter.onEvent(modeMsg); + + AttributeChangedMessage hueMsg = new AttributeChangedMessage(); + hueMsg.path = new Path(); + hueMsg.path.attributeName = "currentHue"; + hueMsg.value = 127; // ~180 degrees + converter.onEvent(hueMsg); + + AttributeChangedMessage satMsg = new AttributeChangedMessage(); + satMsg.path = new Path(); + satMsg.path.attributeName = "currentSaturation"; + satMsg.value = 254; // 100% + converter.onEvent(satMsg); + + verify(mockHandler, times(1)).updateState(eq(1), eq("colorcontrol-color"), + eq(new HSBType(new DecimalType(180), new PercentType(100), new PercentType(100)))); + } + + @Test + void testOnEventWithColorTemperature() throws InterruptedException { + converter.supportsColorTemperature = true; + + AttributeChangedMessage modeMsg = new AttributeChangedMessage(); + modeMsg.path = new Path(); + modeMsg.path.attributeName = "colorMode"; + modeMsg.value = ColorModeEnum.COLOR_TEMPERATURE_MIREDS; + converter.onEvent(modeMsg); + + AttributeChangedMessage tempMsg = new AttributeChangedMessage(); + tempMsg.path = new Path(); + tempMsg.path.attributeName = "colorTemperatureMireds"; + tempMsg.value = 250; + converter.onEvent(tempMsg); + + verify(mockHandler, times(1)).updateState(eq(1), eq("colorcontrol-temperature"), eq(new PercentType(27))); + verify(mockHandler, times(1)).updateState(eq(1), eq("colorcontrol-temperature-abs"), + eq(new QuantityType<>(250, Units.MIRED))); + } + + @Test + void testInitState() throws InterruptedException { + mockColorCluster.colorMode = ColorModeEnum.CURRENT_HUE_AND_CURRENT_SATURATION; + mockColorCluster.currentHue = 127; // ~180 degrees + mockColorCluster.currentSaturation = 254; // 100% + mockColorCluster.colorTemperatureMireds = 250; + mockColorCluster.featureMap.hueSaturation = true; + mockColorCluster.featureMap.colorTemperature = true; + mockLevelCluster.currentLevel = 254; + converter.initState(true, mockLevelCluster); + + verify(mockHandler, times(1)).updateState(eq(1), eq("colorcontrol-color"), + eq(new HSBType(new DecimalType(180), new PercentType(100), new PercentType(100)))); + } + + @Test + void testOnEventWithBrightness() { + AttributeChangedMessage msg = new AttributeChangedMessage(); + msg.path = new Path(); + msg.path.attributeName = "currentLevel"; + msg.value = 254; // 100% + converter.onEvent(msg); + + verify(mockHandler, times(1)).updateState(eq(1), eq("colorcontrol-color"), eq(new HSBType("0,0,100"))); + } + + @Test + void testOnEventWithOnOff() { + mockColorCluster.colorMode = ColorModeEnum.CURRENT_HUE_AND_CURRENT_SATURATION; + mockColorCluster.currentHue = 127; // ~180 degrees + mockColorCluster.currentSaturation = 254; // 100% + mockLevelCluster.currentLevel = 0; + converter.initState(false, mockLevelCluster); + + AttributeChangedMessage msg = new AttributeChangedMessage(); + msg.path = new Path(); + msg.path.attributeName = "onOff"; + msg.value = false; + converter.onEvent(msg); + + verify(mockHandler, atLeastOnce()).updateState(eq(1), eq("colorcontrol-color"), eq(new HSBType("180,100,0"))); // Sam + } + + @Test + void testOnEventWithOnOffFollowedByLevel() { + mockColorCluster.colorMode = ColorModeEnum.CURRENT_HUE_AND_CURRENT_SATURATION; + mockColorCluster.currentHue = 127; // ~180 degrees + mockColorCluster.currentSaturation = 254; // 100% + mockLevelCluster.currentLevel = 254; + converter.initState(true, mockLevelCluster); + + AttributeChangedMessage offMsg = new AttributeChangedMessage(); + offMsg.path = new Path(); + offMsg.path.attributeName = "onOff"; + offMsg.value = false; + converter.onEvent(offMsg); + + // Then change level while off + AttributeChangedMessage levelMsg = new AttributeChangedMessage(); + levelMsg.path = new Path(); + levelMsg.path.attributeName = "currentLevel"; + levelMsg.value = 254; // 100% + converter.onEvent(levelMsg); + + // Verify brightness remains 0 since device is off (2 updates, one for onOff, one for level) + verify(mockHandler, times(2)).updateState(eq(1), eq("colorcontrol-color"), eq(new HSBType("180,100,0"))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java new file mode 100644 index 00000000000..abd3ea9b8f0 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/DoorLockConverterTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.DoorLockCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for DoorLockConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class DoorLockConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private DoorLockCluster mockCluster; + + @NonNullByDefault({}) + private DoorLockConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + converter = new DoorLockConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#doorlock-lockstate", channel.getUID().toString()); + assertEquals("Switch", channel.getAcceptedItemType()); + } + + @Test + void testHandleCommandLock() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#doorlock-lockstate"); + converter.handleCommand(channelUID, OnOffType.ON); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(DoorLockCluster.CLUSTER_NAME), + eq(DoorLockCluster.lockDoor(null))); + } + + @Test + void testHandleCommandUnlock() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#doorlock-lockstate"); + converter.handleCommand(channelUID, OnOffType.OFF); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(DoorLockCluster.CLUSTER_NAME), + eq(DoorLockCluster.unlockDoor(null))); + } + + @Test + void testOnEventWithLockState() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "lockState"; + message.value = DoorLockCluster.LockStateEnum.LOCKED; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(OnOffType.ON)); + } + + @Test + void testInitState() { + mockCluster.lockState = DoorLockCluster.LockStateEnum.LOCKED; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("doorlock-lockstate"), eq(OnOffType.ON)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalEnergyMeasurementConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalEnergyMeasurementConverterTest.java new file mode 100644 index 00000000000..b7ed0b4d551 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalEnergyMeasurementConverterTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.math.BigInteger; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ElectricalEnergyMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for ElectricalEnergyMeasurementConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ElectricalEnergyMeasurementConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private ElectricalEnergyMeasurementCluster mockCluster; + @NonNullByDefault({}) + private ElectricalEnergyMeasurementConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.featureMap = new ElectricalEnergyMeasurementCluster.FeatureMap(true, true, true, true); + converter = new ElectricalEnergyMeasurementConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(4, channels.size()); + + for (Channel channel : channels.keySet()) { + assertEquals("Number:Energy", channel.getAcceptedItemType()); + } + } + + @Test + void testOnEventWithCumulativeEnergyImported() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "cumulativeEnergyImported"; + + ElectricalEnergyMeasurementCluster.EnergyMeasurementStruct energyMeasurement = mockCluster.new EnergyMeasurementStruct( + BigInteger.valueOf(1000), null, null, null, null); + message.value = energyMeasurement; + + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), + eq("electricalenergymeasurement-cumulativeenergyimported-energy"), + eq(new QuantityType<>(1.0, Units.WATT_HOUR))); + } + + @Test + void testInitState() { + ElectricalEnergyMeasurementCluster.EnergyMeasurementStruct measurement = mockCluster.new EnergyMeasurementStruct( + BigInteger.valueOf(1000), null, null, null, null); + + mockCluster.cumulativeEnergyImported = measurement; + mockCluster.periodicEnergyImported = measurement; + + converter.initState(); + + verify(mockHandler, times(1)).updateState(eq(1), + eq("electricalenergymeasurement-cumulativeenergyimported-energy"), + eq(new QuantityType<>(1.0, Units.WATT_HOUR))); + verify(mockHandler, times(1)).updateState(eq(1), + eq("electricalenergymeasurement-periodicenergyimported-energy"), + eq(new QuantityType<>(1.0, Units.WATT_HOUR))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalPowerMeasurementConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalPowerMeasurementConverterTest.java new file mode 100644 index 00000000000..b2dae879bc4 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ElectricalPowerMeasurementConverterTest.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.math.BigInteger; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ElectricalPowerMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * Test class for ElectricalPowerMeasurementConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ElectricalPowerMeasurementConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private ElectricalPowerMeasurementCluster mockCluster; + @NonNullByDefault({}) + private ElectricalPowerMeasurementConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.activeCurrent = BigInteger.valueOf(1000); // 1A + mockCluster.voltage = BigInteger.valueOf(230000); // 230V + converter = new ElectricalPowerMeasurementConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(3, channels.size()); + + for (Channel channel : channels.keySet()) { + String channelId = channel.getUID().getIdWithoutGroup(); + switch (channelId) { + case "electricalpowermeasurement-activepower": + assertEquals("Number:Power", channel.getAcceptedItemType()); + break; + case "electricalpowermeasurement-activecurrent": + assertEquals("Number:ElectricCurrent", channel.getAcceptedItemType()); + break; + case "electricalpowermeasurement-voltage": + assertEquals("Number:ElectricPotential", channel.getAcceptedItemType()); + break; + } + } + } + + @Test + void testOnEventWithActivePower() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "activePower"; + message.value = 230000; // 230W + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-activepower"), + eq(new QuantityType<>(230.0, Units.WATT))); + } + + @Test + void testOnEventWithActiveCurrent() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "activeCurrent"; + message.value = 1000; // 1A + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-activecurrent"), + eq(new QuantityType<>(1.0, Units.AMPERE))); + } + + @Test + void testOnEventWithVoltage() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "voltage"; + message.value = 230000; // 230V + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-voltage"), + eq(new QuantityType<>(230.0, Units.VOLT))); + } + + @Test + void testOnEventWithInvalidValue() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "activePower"; + message.value = "invalid"; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-activepower"), + eq(UnDefType.UNDEF)); + } + + @Test + void testInitState() { + mockCluster.activePower = BigInteger.valueOf(230000); // 230W + mockCluster.activeCurrent = BigInteger.valueOf(1000); // 1A + mockCluster.voltage = BigInteger.valueOf(230000); // 230V + converter.initState(); + + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-activepower"), + eq(new QuantityType<>(230.0, Units.WATT))); + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-activecurrent"), + eq(new QuantityType<>(1.0, Units.AMPERE))); + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-voltage"), + eq(new QuantityType<>(230.0, Units.VOLT))); + } + + @Test + void testInitStateWithNullValues() { + mockCluster.activePower = null; + mockCluster.activeCurrent = null; + mockCluster.voltage = null; + converter.initState(); + + verify(mockHandler, times(1)).updateState(eq(1), eq("electricalpowermeasurement-activepower"), + eq(UnDefType.NULL)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/FanControlConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/FanControlConverterTest.java new file mode 100644 index 00000000000..f8d49ceacce --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/FanControlConverterTest.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.FanControlCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.IncreaseDecreaseType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for FanControlConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class FanControlConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private FanControlCluster mockCluster; + @NonNullByDefault({}) + private FanControlConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.fanModeSequence = FanControlCluster.FanModeSequenceEnum.OFF_LOW_MED_HIGH_AUTO; + converter = new FanControlConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(2, channels.size()); + + for (Channel channel : channels.keySet()) { + String channelId = channel.getUID().getIdWithoutGroup(); + switch (channelId) { + case "fancontrol-fanmode": + assertEquals("Number", channel.getAcceptedItemType()); + break; + case "fancontrol-percent": + assertEquals("Dimmer", channel.getAcceptedItemType()); + break; + } + } + } + + @Test + void testHandleCommandMode() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#fancontrol-fanmode"); + converter.handleCommand(channelUID, new DecimalType(1)); // Low mode + verify(mockHandler, times(1)).writeAttribute(eq(1), eq(FanControlCluster.CLUSTER_NAME), eq("fanMode"), eq("1")); + } + + @Test + void testHandleCommandPercent() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#fancontrol-percent"); + converter.handleCommand(channelUID, new PercentType(50)); + verify(mockHandler, times(1)).writeAttribute(eq(1), eq(FanControlCluster.CLUSTER_NAME), eq("percentSetting"), + eq("50")); + } + + @Test + void testHandleCommandIncrease() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#fancontrol-percent"); + converter.handleCommand(channelUID, IncreaseDecreaseType.INCREASE); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(FanControlCluster.CLUSTER_NAME), + eq(FanControlCluster.step(FanControlCluster.StepDirectionEnum.INCREASE, false, false))); + } + + @Test + void testHandleCommandDecrease() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#fancontrol-percent"); + converter.handleCommand(channelUID, IncreaseDecreaseType.DECREASE); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(FanControlCluster.CLUSTER_NAME), + eq(FanControlCluster.step(FanControlCluster.StepDirectionEnum.DECREASE, false, true))); + } + + @Test + void testOnEventWithFanMode() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "fanMode"; + message.value = FanControlCluster.FanModeEnum.LOW; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("fancontrol-fanmode"), eq(new DecimalType(1))); + } + + @Test + void testOnEventWithPercentSetting() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "percentSetting"; + message.value = 50; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("fancontrol-percent"), eq(new PercentType(50))); + } + + @Test + void testInitState() { + mockCluster.fanMode = FanControlCluster.FanModeEnum.LOW; + mockCluster.percentSetting = 50; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("fancontrol-fanmode"), + eq(new DecimalType(mockCluster.fanMode.value))); + verify(mockHandler, times(1)).updateState(eq(1), eq("fancontrol-percent"), eq(new PercentType(50))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/GenericConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/GenericConverterTest.java new file mode 100644 index 00000000000..504d4c2d539 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/GenericConverterTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import javax.measure.quantity.Temperature; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; +import org.openhab.binding.matter.internal.util.ValueUtils; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.ImperialUnits; +import org.openhab.core.library.unit.SIUnits; + +/** + * Test class for GenericConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class GenericConverterTest { + + @Test + void testLevelToPercent() { + assertEquals(PercentType.ZERO, ValueUtils.levelToPercent(0)); + assertEquals(new PercentType(1), ValueUtils.levelToPercent(1)); + assertEquals(new PercentType(50), ValueUtils.levelToPercent(127)); + assertEquals(new PercentType(100), ValueUtils.levelToPercent(254)); + } + + @Test + void testPercentToLevel() { + assertEquals(0, ValueUtils.percentToLevel(PercentType.ZERO)); + assertEquals(127, ValueUtils.percentToLevel(new PercentType(50))); + assertEquals(254, ValueUtils.percentToLevel(PercentType.HUNDRED)); + } + + @Test + void testTemperatureToValueCelsius() { + assertEquals(2000, ValueUtils.temperatureToValue(new QuantityType(20.0, SIUnits.CELSIUS))); + assertEquals(-500, ValueUtils.temperatureToValue(new QuantityType(-5.0, SIUnits.CELSIUS))); + assertEquals(2250, ValueUtils.temperatureToValue(new QuantityType(22.5, SIUnits.CELSIUS))); + } + + @Test + void testTemperatureToValueFahrenheit() { + assertEquals(0, ValueUtils.temperatureToValue(new QuantityType(32.0, ImperialUnits.FAHRENHEIT))); + assertEquals(2000, + ValueUtils.temperatureToValue(new QuantityType(68.0, ImperialUnits.FAHRENHEIT))); + } + + @Test + void testTemperatureToValueNumber() { + assertEquals(2000, ValueUtils.temperatureToValue(new DecimalType(20))); + assertEquals(-500, ValueUtils.temperatureToValue(new DecimalType(-5))); + } + + @Test + void testTemperatureToValueInvalid() { + assertNull(ValueUtils.temperatureToValue(new QuantityType<>(20.0, ImperialUnits.MILES_PER_HOUR))); + } + + @Test + void testValueToTemperature() { + assertEquals(new QuantityType(20.0, SIUnits.CELSIUS), ValueUtils.valueToTemperature(2000)); + assertEquals(new QuantityType(-5.0, SIUnits.CELSIUS), ValueUtils.valueToTemperature(-500)); + assertEquals(new QuantityType(22.5, SIUnits.CELSIUS), ValueUtils.valueToTemperature(2250)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/IlluminanceMeasurementConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/IlluminanceMeasurementConverterTest.java new file mode 100644 index 00000000000..e8a9de8f5fa --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/IlluminanceMeasurementConverterTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import javax.measure.quantity.Illuminance; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.IlluminanceMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * Test class for IlluminanceMeasurementConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class IlluminanceMeasurementConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private IlluminanceMeasurementCluster mockCluster; + @NonNullByDefault({}) + private IlluminanceMeasurementConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + converter = new IlluminanceMeasurementConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#illuminancemeasurement-measuredvalue", channel.getUID().toString()); + assertEquals("Number:Illuminance", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithMeasuredValue() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "measuredValue"; + message.value = 100; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("illuminancemeasurement-measuredvalue"), + eq(new QuantityType(100, Units.LUX))); + } + + @Test + void testInitState() { + mockCluster.measuredValue = 100; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("illuminancemeasurement-measuredvalue"), + eq(new QuantityType(100, Units.LUX))); + } + + @Test + void testInitStateWithNullValue() { + mockCluster.measuredValue = null; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("illuminancemeasurement-measuredvalue"), + eq(UnDefType.NULL)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/LevelControlConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/LevelControlConverterTest.java new file mode 100644 index 00000000000..831ab27e0b3 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/LevelControlConverterTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.LevelControlCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for LevelControlConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class LevelControlConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private LevelControlCluster mockCluster; + @NonNullByDefault({}) + private LevelControlConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + converter = new LevelControlConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#levelcontrol-level", channel.getUID().toString()); + assertEquals("Dimmer", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithLevel() { + AttributeChangedMessage levelMessage = new AttributeChangedMessage(); + levelMessage.path = new Path(); + levelMessage.path.attributeName = LevelControlCluster.ATTRIBUTE_CURRENT_LEVEL; + levelMessage.value = 254; + + // Set lastOnOff to ON to ensure level update is processed + AttributeChangedMessage onOffMessage = new AttributeChangedMessage(); + onOffMessage.path = new Path(); + onOffMessage.path.attributeName = OnOffCluster.ATTRIBUTE_ON_OFF; + onOffMessage.value = Boolean.TRUE; + converter.onEvent(onOffMessage); + + converter.onEvent(levelMessage); + verify(mockHandler, times(1)).updateState(eq(1), eq("levelcontrol-level"), eq(new PercentType(100))); + } + + @Test + void testOffEventWithOnOff() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = OnOffCluster.ATTRIBUTE_ON_OFF; + message.value = Boolean.FALSE; + + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("levelcontrol-level"), eq(OnOffType.OFF)); + } + + @Test + void testInitState() { + mockCluster.currentLevel = 254; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("levelcontrol-level"), eq(new PercentType(100))); + } + + @Test + void testInitStateOff() { + mockCluster.currentLevel = 254; + converter.initState(false); + verify(mockHandler, times(1)).updateState(eq(1), eq("levelcontrol-level"), eq(OnOffType.OFF)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ModeSelectConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ModeSelectConverterTest.java new file mode 100644 index 00000000000..646b1d6075e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ModeSelectConverterTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ModeSelectCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for ModeSelectConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ModeSelectConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private ModeSelectCluster mockCluster; + @NonNullByDefault({}) + private ModeSelectConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + // Setup supported modes + List supportedModes = new ArrayList<>(); + supportedModes.add(mockCluster.new ModeOptionStruct("Mode 1", 1, null)); + supportedModes.add(mockCluster.new ModeOptionStruct("Mode 2", 2, null)); + mockCluster.supportedModes = supportedModes; + mockCluster.description = "Test Mode Select"; + + converter = new ModeSelectConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + @SuppressWarnings("null") + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#modeselect-mode", channel.getUID().toString()); + assertEquals("Number", channel.getAcceptedItemType()); + + StateDescription stateDescription = channels.get(channel); + assertEquals(2, stateDescription.getOptions().size()); + assertEquals("1", stateDescription.getOptions().get(0).getValue()); + assertEquals("Mode 1", stateDescription.getOptions().get(0).getLabel()); + assertEquals("2", stateDescription.getOptions().get(1).getValue()); + assertEquals("Mode 2", stateDescription.getOptions().get(1).getLabel()); + } + + @Test + void testHandleCommand() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#modeselect-mode"); + converter.handleCommand(channelUID, new DecimalType(1)); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(ModeSelectCluster.CLUSTER_NAME), + eq(ModeSelectCluster.changeToMode(1))); + } + + @Test + void testOnEventWithCurrentMode() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "currentMode"; + message.value = 1; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("modeselect-mode"), eq(new DecimalType(1))); + } + + @Test + void testInitState() { + mockCluster.currentMode = 1; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("modeselect-mode"), eq(new DecimalType(1))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/OccupancySensingConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/OccupancySensingConverterTest.java new file mode 100644 index 00000000000..5ac34aef8f6 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/OccupancySensingConverterTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OccupancySensingCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for OccupancySensingConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class OccupancySensingConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private OccupancySensingCluster mockCluster; + @NonNullByDefault({}) + private OccupancySensingConverter converter; + + @Override + @BeforeEach + @SuppressWarnings("null") + void setUp() { + super.setUp(); + OccupancySensingCluster.OccupancyBitmap bitmap = Mockito.mock(OccupancySensingCluster.OccupancyBitmap.class); + bitmap.occupied = true; + mockCluster.occupancy = bitmap; + converter = new OccupancySensingConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#occupancysensing-occupied", channel.getUID().toString()); + assertEquals("Switch", channel.getAcceptedItemType()); + } + + @Test + @SuppressWarnings("null") + void testOnEventWithOccupancy() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "occupancy"; + OccupancySensingCluster.OccupancyBitmap bitmap = Mockito.mock(OccupancySensingCluster.OccupancyBitmap.class); + bitmap.occupied = true; + message.value = bitmap; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("occupancysensing-occupied"), eq(OnOffType.ON)); + } + + @Test + @SuppressWarnings("null") + void testInitState() { + OccupancySensingCluster.OccupancyBitmap bitmap = Mockito.mock(OccupancySensingCluster.OccupancyBitmap.class); + bitmap.occupied = true; + mockCluster.occupancy = bitmap; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("occupancysensing-occupied"), eq(OnOffType.ON)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/OnOffConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/OnOffConverterTest.java new file mode 100644 index 00000000000..572eeb38901 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/OnOffConverterTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.OnOffCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for OnOffConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class OnOffConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private OnOffCluster mockCluster; + @NonNullByDefault({}) + private OnOffConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + converter = new OnOffConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#onoffcontrol-onoff", channel.getUID().toString()); + assertEquals("Switch", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithOnOff() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "onOff"; + message.value = true; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("onoffcontrol-onoff"), eq(OnOffType.ON)); + } + + @Test + void testOnEventWithOnOffFalse() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "onOff"; + message.value = false; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("onoffcontrol-onoff"), eq(OnOffType.OFF)); + } + + @Test + void testInitState() { + mockCluster.onOff = true; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("onoffcontrol-onoff"), eq(OnOffType.ON)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/PowerSourceConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/PowerSourceConverterTest.java new file mode 100644 index 00000000000..15bc41dfbdd --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/PowerSourceConverterTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import javax.measure.quantity.Dimensionless; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.PowerSourceCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * Test class for PowerSourceConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class PowerSourceConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private PowerSourceCluster mockCluster; + @NonNullByDefault({}) + private PowerSourceConverter converter; + + @Override + @BeforeEach + @SuppressWarnings("null") + void setUp() { + super.setUp(); + mockCluster.featureMap = Mockito.mock(PowerSourceCluster.FeatureMap.class); + mockCluster.featureMap.battery = true; + mockCluster.batPercentRemaining = 100; // 50% + mockCluster.batChargeLevel = PowerSourceCluster.BatChargeLevelEnum.OK; + converter = new PowerSourceConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + @SuppressWarnings("null") + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(2, channels.size()); + + for (Channel channel : channels.keySet()) { + String channelId = channel.getUID().getIdWithoutGroup(); + switch (channelId) { + case "powersource-batpercentremaining": + assertEquals("Number:Dimensionless", channel.getAcceptedItemType()); + break; + case "powersource-batchargelevel": + assertEquals("Number", channel.getAcceptedItemType()); + StateDescription stateDescription = channels.get(channel); + assertEquals(3, stateDescription.getOptions().size()); // Number of BatChargeLevelEnum values + break; + } + } + } + + @Test + void testOnEventWithBatteryPercent() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "batPercentRemaining"; + message.value = 100; // 50% + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("powersource-batpercentremaining"), + eq(new QuantityType(50, Units.PERCENT))); + } + + @Test + void testOnEventWithChargeLevel() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "batChargeLevel"; + message.value = PowerSourceCluster.BatChargeLevelEnum.OK; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("powersource-batchargelevel"), + eq(new DecimalType(PowerSourceCluster.BatChargeLevelEnum.OK.getValue()))); + } + + @Test + void testOnEventWithInvalidBatteryPercent() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "batPercentRemaining"; + message.value = 201; // Invalid value + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("powersource-batpercentremaining"), eq(UnDefType.UNDEF)); + } + + @Test + void testInitState() { + mockCluster.batPercentRemaining = 100; // 50% + mockCluster.batChargeLevel = PowerSourceCluster.BatChargeLevelEnum.OK; + converter.initState(); + + verify(mockHandler, times(1)).updateState(eq(1), eq("powersource-batpercentremaining"), + eq(new QuantityType(50, Units.PERCENT))); + verify(mockHandler, times(1)).updateState(eq(1), eq("powersource-batchargelevel"), + eq(new DecimalType(PowerSourceCluster.BatChargeLevelEnum.OK.getValue()))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/RelativeHumidityMeasurementConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/RelativeHumidityMeasurementConverterTest.java new file mode 100644 index 00000000000..16b8cef2d4e --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/RelativeHumidityMeasurementConverterTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import javax.measure.quantity.Dimensionless; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.RelativeHumidityMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for RelativeHumidityMeasurementConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class RelativeHumidityMeasurementConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private RelativeHumidityMeasurementCluster mockCluster; + @NonNullByDefault({}) + private RelativeHumidityMeasurementConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.measuredValue = 5000; // 50.00% + converter = new RelativeHumidityMeasurementConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#relativehumiditymeasurement-measuredvalue", channel.getUID().toString()); + assertEquals("Number:Dimensionless", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithMeasuredValue() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "measuredValue"; + message.value = 5000; // 50.00% + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("relativehumiditymeasurement-measuredvalue"), + eq(new QuantityType(50.00, Units.PERCENT))); + } + + @Test + void testInitState() { + mockCluster.measuredValue = 5000; // 50.00% + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("relativehumiditymeasurement-measuredvalue"), + eq(new QuantityType(50.00, Units.PERCENT))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/SwitchConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/SwitchConverterTest.java new file mode 100644 index 00000000000..3985b66d32c --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/SwitchConverterTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.SwitchCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.EventTriggeredMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.binding.matter.internal.client.dto.ws.TriggerEvent; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.type.ChannelKind; +import org.openhab.core.types.StateDescription; + +/** + * Test class for SwitchConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class SwitchConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private SwitchCluster mockCluster; + @NonNullByDefault({}) + private SwitchConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.featureMap = new SwitchCluster.FeatureMap(true, true, true, true, true, true); + mockCluster.numberOfPositions = 2; + converter = new SwitchConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + + // Should create channels for switch position and all trigger events + assertEquals(8, channels.size()); + + boolean hasStateChannel = false; + int triggerChannels = 0; + + for (Channel channel : channels.keySet()) { + if (channel.getKind() == ChannelKind.STATE) { + hasStateChannel = true; + assertEquals("Number", channel.getAcceptedItemType()); + assertEquals("switch-switch", channel.getUID().getIdWithoutGroup()); + } else if (channel.getKind() == ChannelKind.TRIGGER) { + triggerChannels++; + } + } + + assertTrue(hasStateChannel); + assertEquals(7, triggerChannels); // All trigger channels enabled + } + + @Test + void testOnEventWithCurrentPosition() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "currentPosition"; + message.value = 1; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("switch-switch"), eq(new DecimalType(1))); + } + + @Test + void testOnEventWithTrigger() { + EventTriggeredMessage message = new EventTriggeredMessage(); + message.path = new Path(); + message.path.eventName = "SwitchLatched"; + TriggerEvent event = new TriggerEvent(); + event.data = "{\"newPosition\": 1}"; + message.events = new TriggerEvent[] { event }; + converter.onEvent(message); + verify(mockHandler, times(1)).triggerChannel(eq(1), eq("switch-switchlatched"), + eq("\"{\\\"newPosition\\\": 1}\"")); + } + + @Test + void testInitState() { + mockCluster.currentPosition = 1; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("switch-switch"), eq(new DecimalType(1))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/TemperatureMeasurementConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/TemperatureMeasurementConverterTest.java new file mode 100644 index 00000000000..4f6e9f90a09 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/TemperatureMeasurementConverterTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.TemperatureMeasurementCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for TemperatureMeasurementConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class TemperatureMeasurementConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private TemperatureMeasurementCluster mockCluster; + @NonNullByDefault({}) + private TemperatureMeasurementConverter converter; + + @BeforeEach + @SuppressWarnings("null") + void setUp() { + super.setUp(); + converter = new TemperatureMeasurementConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#temperaturemeasurement-measuredvalue", channel.getUID().toString()); + assertEquals("Number:Temperature", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithMeasuredValue() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "measuredValue"; + message.value = 2000; // 20°C + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("temperaturemeasurement-measuredvalue"), + eq(new QuantityType<>(20.0, SIUnits.CELSIUS))); + } + + @Test + void testInitState() { + mockCluster.measuredValue = 2000; // 20°C + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("temperaturemeasurement-measuredvalue"), + eq(new QuantityType<>(20.0, SIUnits.CELSIUS))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/TestMatterBaseThingHandler.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/TestMatterBaseThingHandler.java new file mode 100644 index 00000000000..a94d2638ff9 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/TestMatterBaseThingHandler.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.matter.internal.MatterChannelTypeProvider; +import org.openhab.binding.matter.internal.MatterConfigDescriptionProvider; +import org.openhab.binding.matter.internal.MatterStateDescriptionOptionProvider; +import org.openhab.binding.matter.internal.bridge.MatterBridgeClient; +import org.openhab.binding.matter.internal.controller.MatterControllerClient; +import org.openhab.binding.matter.internal.handler.MatterBaseThingHandler; +import org.openhab.binding.matter.internal.util.TranslationService; +import org.openhab.core.thing.ThingTypeUID; +import org.openhab.core.thing.binding.BaseThingHandlerFactory; +import org.openhab.core.thing.binding.builder.ThingBuilder; + +/** + * Test handler for converter tests + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +public class TestMatterBaseThingHandler extends MatterBaseThingHandler { + public static final ThingTypeUID THING_TYPE_TEST = new ThingTypeUID("matter", "test"); + + private final MatterControllerClient mockClient; + + public TestMatterBaseThingHandler(MatterBridgeClient bridgeClient, BaseThingHandlerFactory thingHandlerFactory, + MatterStateDescriptionOptionProvider stateDescriptionProvider, + MatterChannelTypeProvider channelTypeProvider, MatterConfigDescriptionProvider configDescriptionProvider, + TranslationService translationService) { + super(ThingBuilder.create(THING_TYPE_TEST, "test").build(), thingHandlerFactory, stateDescriptionProvider, + channelTypeProvider, configDescriptionProvider, translationService); + this.mockClient = new MatterControllerClient(); + } + + @Override + public @Nullable MatterControllerClient getClient() { + return mockClient; + } + + @Override + public void updateState(String channelId, org.openhab.core.types.State state) { + super.updateState(channelId, state); + } + + @Override + public void triggerChannel(String channelId, String event) { + super.triggerChannel(channelId, event); + } + + @Override + public BigInteger getNodeId() { + return BigInteger.ONE; + } + + @Override + public ThingTypeUID getDynamicThingTypeUID() { + return THING_TYPE_TEST; + } + + @Override + public boolean isBridgeType() { + return false; + } + + @Override + public Integer getPollInterval() { + return 0; + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ThermostatConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ThermostatConverterTest.java new file mode 100644 index 00000000000..7ef89903475 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ThermostatConverterTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThermostatCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.SIUnits; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.Command; +import org.openhab.core.types.StateDescription; + +/** + * Test class for ThermostatConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ThermostatConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private ThermostatCluster mockCluster; + @NonNullByDefault({}) + private ThermostatConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.featureMap = new ThermostatCluster.FeatureMap(true, true, true, false, false, true, false, false, + false); + mockCluster.absMinHeatSetpointLimit = 500; // 5°C + mockCluster.absMaxHeatSetpointLimit = 3000; // 30°C + mockCluster.absMinCoolSetpointLimit = 1500; // 15°C + mockCluster.absMaxCoolSetpointLimit = 3500; // 35°C + converter = new ThermostatConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + + assertEquals(6, channels.size()); + + for (Channel channel : channels.keySet()) { + String channelId = channel.getUID().getIdWithoutGroup(); + switch (channelId) { + case "thermostat-systemmode": + assertEquals("Number", channel.getAcceptedItemType()); + break; + case "thermostat-localtemperature": + case "thermostat-occupiedheating": + case "thermostat-occupiedcooling": + case "thermostat-unoccupiedheating": + case "thermostat-unoccupiedcooling": + assertEquals("Number:Temperature", channel.getAcceptedItemType()); + break; + } + } + } + + @Test + void testHandleCommandSystemMode() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#thermostat-systemmode"); + Command command = new DecimalType(1); // Heat mode + converter.handleCommand(channelUID, command); + verify(mockHandler, times(1)).writeAttribute(eq(1), eq(ThermostatCluster.CLUSTER_NAME), eq("systemMode"), + eq("1")); + } + + @Test + void testHandleCommandHeatingSetpoint() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#thermostat-occupiedheating"); + Command command = new QuantityType<>(20, SIUnits.CELSIUS); + converter.handleCommand(channelUID, command); + verify(mockHandler, times(1)).writeAttribute(eq(1), eq(ThermostatCluster.CLUSTER_NAME), + eq("occupiedHeatingSetpoint"), eq("2000")); + } + + @Test + void testOnEventWithLocalTemperature() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "localTemperature"; + message.value = 2000; // 20°C + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("thermostat-localtemperature"), + eq(new QuantityType<>(20.0, SIUnits.CELSIUS))); + } + + @Test + void testOnEventWithSystemMode() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "systemMode"; + message.value = ThermostatCluster.SystemModeEnum.HEAT; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("thermostat-systemmode"), + eq(new DecimalType(ThermostatCluster.SystemModeEnum.HEAT.value))); + } + + @Test + void testInitState() { + mockCluster.localTemperature = 2000; // 20°C + mockCluster.systemMode = ThermostatCluster.SystemModeEnum.HEAT; + mockCluster.occupiedHeatingSetpoint = 2200; // 22°C + mockCluster.occupiedCoolingSetpoint = 2500; // 25°C + + converter.initState(); + + verify(mockHandler, times(1)).updateState(eq(1), eq("thermostat-localtemperature"), + eq(new QuantityType<>(20.0, SIUnits.CELSIUS))); + verify(mockHandler, times(1)).updateState(eq(1), eq("thermostat-systemmode"), + eq(new DecimalType(mockCluster.systemMode.value))); + verify(mockHandler, times(1)).updateState(eq(1), eq("thermostat-occupiedheating"), + eq(new QuantityType<>(22.0, SIUnits.CELSIUS))); + verify(mockHandler, times(1)).updateState(eq(1), eq("thermostat-occupiedcooling"), + eq(new QuantityType<>(25.0, SIUnits.CELSIUS))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadNetworkDiagnosticsConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadNetworkDiagnosticsConverterTest.java new file mode 100644 index 00000000000..dbca178e48f --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/ThreadNetworkDiagnosticsConverterTest.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.math.BigInteger; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThreadNetworkDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.ThreadNetworkDiagnosticsCluster.RoutingRoleEnum; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; + +/** + * Test class for ThreadNetworkDiagnosticsConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ThreadNetworkDiagnosticsConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private ThreadNetworkDiagnosticsCluster mockCluster; + @NonNullByDefault({}) + private ThreadNetworkDiagnosticsConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.channel = 15; + mockCluster.routingRole = ThreadNetworkDiagnosticsCluster.RoutingRoleEnum.LEADER; + mockCluster.networkName = "TestNetwork"; + mockCluster.panId = 0x1234; // 4660 + mockCluster.extendedPanId = BigInteger.valueOf(223372036854775807L); + mockCluster.rloc16 = 0xABCD; // 43981 + converter = Mockito.spy(new ThreadNetworkDiagnosticsConverter(mockCluster, mockHandler, 1, "TestLabel")); + } + + @Test + void testOnEventWithRoutingRole() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTING_ROLE; + message.value = RoutingRoleEnum.LEADER; + converter.onEvent(message); + verify(converter, times(1)).updateThingAttributeProperty( + eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTING_ROLE), + eq(ThreadNetworkDiagnosticsCluster.RoutingRoleEnum.LEADER)); + } + + @Test + void testOnEventWithNetworkName() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NETWORK_NAME; + message.value = "TestNetwork"; + converter.onEvent(message); + verify(converter, times(1)).updateThingAttributeProperty( + eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NETWORK_NAME), eq("TestNetwork")); + } + + @Test + void testOnEventWithPanId() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = ThreadNetworkDiagnosticsCluster.ATTRIBUTE_PAN_ID; + message.value = 0x1234; + converter.onEvent(message); + verify(converter, times(1)).updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_PAN_ID), + eq(4660)); + } + + @Test + void testOnEventWithExtendedPanId() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = ThreadNetworkDiagnosticsCluster.ATTRIBUTE_EXTENDED_PAN_ID; + message.value = 223372036854775807L; + converter.onEvent(message); + verify(converter, times(1)).updateThingAttributeProperty( + eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_EXTENDED_PAN_ID), eq(223372036854775807L)); + } + + @Test + void testOnEventWithRloc16() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = ThreadNetworkDiagnosticsCluster.ATTRIBUTE_RLOC16; + message.value = 0xABCD; + converter.onEvent(message); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_RLOC16), eq(43981)); + } + + @Test + void testInitState() { + converter.initState(); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_CHANNEL), eq(15)); + verify(converter, atLeastOnce()).updateThingAttributeProperty( + eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTING_ROLE), + eq(ThreadNetworkDiagnosticsCluster.RoutingRoleEnum.LEADER)); + verify(converter, atLeastOnce()).updateThingAttributeProperty( + eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NETWORK_NAME), eq("TestNetwork")); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_PAN_ID), eq(4660)); + verify(converter, atLeastOnce()).updateThingAttributeProperty( + eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_EXTENDED_PAN_ID), + eq(BigInteger.valueOf(223372036854775807L))); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_RLOC16), eq(43981)); + } + + @Test + void testInitStateWithNullValues() { + mockCluster.channel = null; + mockCluster.routingRole = null; + mockCluster.networkName = null; + mockCluster.panId = null; + mockCluster.extendedPanId = null; + mockCluster.rloc16 = null; + converter.initState(); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_CHANNEL), eq(null)); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_ROUTING_ROLE), eq(null)); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_NETWORK_NAME), eq(null)); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_PAN_ID), eq(null)); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_EXTENDED_PAN_ID), eq(null)); + verify(converter, atLeastOnce()) + .updateThingAttributeProperty(eq(ThreadNetworkDiagnosticsCluster.ATTRIBUTE_RLOC16), eq(null)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/WiFiNetworkDiagnosticsConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/WiFiNetworkDiagnosticsConverterTest.java new file mode 100644 index 00000000000..7db3563d716 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/WiFiNetworkDiagnosticsConverterTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WiFiNetworkDiagnosticsCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.QuantityType; +import org.openhab.core.library.unit.Units; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.types.StateDescription; +import org.openhab.core.types.UnDefType; + +/** + * Test class for WiFiNetworkDiagnosticsConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class WiFiNetworkDiagnosticsConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private WiFiNetworkDiagnosticsCluster mockCluster; + @NonNullByDefault({}) + private WiFiNetworkDiagnosticsConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + mockCluster.rssi = -70; + converter = new WiFiNetworkDiagnosticsConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#wifinetworkdiagnostics-rssi", channel.getUID().toString()); + assertEquals("Number:Power", channel.getAcceptedItemType()); + } + + @Test + void testOnEventWithRssi() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "rssi"; + message.value = -70; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("wifinetworkdiagnostics-rssi"), + eq(new QuantityType<>(-70, Units.DECIBEL_MILLIWATTS))); + } + + @Test + void testOnEventWithNonNumberValue() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "rssi"; + message.value = "invalid"; + converter.onEvent(message); + // Should not call updateState for non-number values + verify(mockHandler, times(0)).updateState(eq(1), eq("wifinetworkdiagnostics-rssi"), + eq(new QuantityType<>(-70, Units.DECIBEL_MILLIWATTS))); + } + + @Test + void testInitState() { + mockCluster.rssi = -70; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("wifinetworkdiagnostics-rssi"), + eq(new QuantityType<>(-70, Units.DECIBEL_MILLIWATTS))); + } + + @Test + void testInitStateWithNullValue() { + mockCluster.rssi = null; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("wifinetworkdiagnostics-rssi"), eq(UnDefType.NULL)); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/WindowCoveringConverterTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/WindowCoveringConverterTest.java new file mode 100644 index 00000000000..bcf5c79c2a7 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/controller/devices/converter/WindowCoveringConverterTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.controller.devices.converter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.openhab.binding.matter.internal.client.dto.cluster.gen.WindowCoveringCluster; +import org.openhab.binding.matter.internal.client.dto.ws.AttributeChangedMessage; +import org.openhab.binding.matter.internal.client.dto.ws.Path; +import org.openhab.core.library.types.PercentType; +import org.openhab.core.library.types.StopMoveType; +import org.openhab.core.library.types.UpDownType; +import org.openhab.core.thing.Channel; +import org.openhab.core.thing.ChannelGroupUID; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.types.StateDescription; + +/** + * Test class for WindowCoveringConverter + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class WindowCoveringConverterTest extends BaseMatterConverterTest { + + @Mock + @NonNullByDefault({}) + private WindowCoveringCluster mockCluster; + @NonNullByDefault({}) + private WindowCoveringConverter converter; + + @Override + @BeforeEach + void setUp() { + super.setUp(); + converter = new WindowCoveringConverter(mockCluster, mockHandler, 1, "TestLabel"); + } + + @Test + void testCreateChannels() { + ChannelGroupUID channelGroupUID = new ChannelGroupUID("matter:node:test:12345:1"); + Map channels = converter.createChannels(channelGroupUID); + assertEquals(1, channels.size()); + Channel channel = channels.keySet().iterator().next(); + assertEquals("matter:node:test:12345:1#windowcovering-lift", channel.getUID().toString()); + assertEquals("Rollershutter", channel.getAcceptedItemType()); + } + + @Test + void testHandleCommandUp() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#windowcovering-lift"); + converter.handleCommand(channelUID, UpDownType.UP); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(WindowCoveringCluster.CLUSTER_NAME), + eq(WindowCoveringCluster.upOrOpen())); + } + + @Test + void testHandleCommandDown() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#windowcovering-lift"); + converter.handleCommand(channelUID, UpDownType.DOWN); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(WindowCoveringCluster.CLUSTER_NAME), + eq(WindowCoveringCluster.downOrClose())); + } + + @Test + void testHandleCommandStop() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#windowcovering-lift"); + converter.handleCommand(channelUID, StopMoveType.STOP); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(WindowCoveringCluster.CLUSTER_NAME), + eq(WindowCoveringCluster.stopMotion())); + } + + @Test + void testHandleCommandPercent() { + ChannelUID channelUID = new ChannelUID("matter:node:test:12345:1#windowcovering-lift"); + converter.handleCommand(channelUID, new PercentType(50)); + verify(mockHandler, times(1)).sendClusterCommand(eq(1), eq(WindowCoveringCluster.CLUSTER_NAME), + eq(WindowCoveringCluster.goToLiftPercentage(50))); + } + + @Test + void testOnEventWithCurrentPositionLiftPercentage() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "currentPositionLiftPercentage"; + message.value = 75; + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("windowcovering-lift"), eq(new PercentType(75))); + } + + @Test + void testOnEventWithCurrentPositionLiftPercent100ths() { + AttributeChangedMessage message = new AttributeChangedMessage(); + message.path = new Path(); + message.path.attributeName = "currentPositionLiftPercent100ths"; + message.value = 7500; // 75.00% + converter.onEvent(message); + verify(mockHandler, times(1)).updateState(eq(1), eq("windowcovering-lift"), eq(new PercentType(75))); + } + + @Test + void testInitStateWithPercentage() { + mockCluster.currentPositionLiftPercentage = 25; + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("windowcovering-lift"), eq(new PercentType(25))); + } + + @Test + void testInitStateWithPercent100ths() { + mockCluster.currentPositionLiftPercent100ths = 2500; // 25.00% + converter.initState(); + verify(mockHandler, times(1)).updateState(eq(1), eq("windowcovering-lift"), eq(new PercentType(25))); + } +} diff --git a/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/util/ThreadDatasetTest.java b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/util/ThreadDatasetTest.java new file mode 100644 index 00000000000..55b00d48660 --- /dev/null +++ b/bundles/org.openhab.binding.matter/src/test/java/org/openhab/binding/matter/internal/util/ThreadDatasetTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010-2025 Contributors to the openHAB project + * + * See the NOTICE file(s) distributed with this work for additional + * information. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.openhab.binding.matter.internal.util; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.junit.jupiter.api.Test; + +/** + * Test for {@link ThreadDataset}. + * + * @author Dan Cunningham - Initial contribution + */ +@NonNullByDefault +class ThreadDatasetTest { + + @Test + void testThreadDatasetCreationAndConversion() { + ThreadDataset ds = new ThreadDataset(); + ds.setPendingTimestamp(0x1122334455667788L); + ds.setDelayTimer(30_000); + ds.setChannel(15); + ds.setChannelMask(134152192L); + ds.setPanId(6699); + ds.setNetworkKey("00112233445566778899AABBCCDDEEFF"); + ds.setPskc("A1A2A3A4A5A6A7A8A9AAABACADAEAFB0"); + ds.setMeshLocalPrefix("FD000DB800000000"); + ds.setNetworkName("openHAB‑Thread"); + + String tlvHex = ds.toHex(); + ThreadDataset back = ThreadDataset.fromHex(tlvHex); + + assertEquals(Optional.of("openHAB‑Thread"), back.getNetworkName()); + assertEquals(Optional.of(15), back.getChannel()); + assertEquals(Optional.of("A1A2A3A4A5A6A7A8A9AAABACADAEAFB0"), back.getPskcHex()); + assertEquals(Optional.of(6699), back.getPanId()); + assertEquals(Optional.of("FD000DB800000000"), back.getMeshLocalPrefixHex()); + assertEquals(Optional.of(134152192L), back.getChannelMask()); + } + + @Test + void testThreadChannelSet() { + ThreadDataset ds = new ThreadDataset(); + Set channels = new LinkedHashSet<>( + Arrays.asList(11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26)); + ds.setChannelSet(channels); + assertEquals(Optional.of(channels), ds.getChannelSet()); + assertEquals(Optional.of(134215680L), ds.getChannelMask()); + } + + @Test + void testThreadDatasetFromHex() { + // This is the dataset from the OpenThread Examples, so well known + String hexInput = "0e080000000000010000000300000f35060004001fffe002084a3bdda4723ed6a00708fd2fca4935b73ecc051000112233445566778899aabbccddeeff030f4f70656e5468726561642d36616462010212340410f2cd947327a3453d03c7bef5b51ea2070c0402a0f7f8"; + ThreadDataset ds = ThreadDataset.fromHex(hexInput); + + assertEquals(Optional.of("OpenThread-6adb"), ds.getNetworkName()); + assertEquals(Optional.of(15), ds.getChannel()); + assertEquals(Optional.of("F2CD947327A3453D03C7BEF5B51EA207"), ds.getPskcHex()); + assertEquals(Optional.of(4660), ds.getPanId()); + assertEquals(Optional.of("4A3BDDA4723ED6A0"), ds.getExtPanIdHex()); + assertEquals(Optional.of("FD2FCA4935B73ECC"), ds.getMeshLocalPrefixHex()); + assertEquals(Optional.of(4294901760L), ds.getChannelMask()); + + Set expectedChannels = Set.of(16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31); + assertEquals(Optional.of(expectedChannels), ds.getChannelSet()); + } + + @Test + void testJsonConversion() { + ThreadDataset ds = new ThreadDataset(); + ds.setNetworkName("TestNetwork"); + ds.setChannel(15); + ds.setPanId(0x1234); + + String json = ds.toJson(); + ThreadDataset back = ThreadDataset.fromJson(json); + assertNotNull(back); + assertEquals(Optional.of("TestNetwork"), back.getNetworkName()); + assertEquals(Optional.of(15), back.getChannel()); + assertEquals(Optional.of(0x1234), back.getPanId()); + } +} diff --git a/bundles/pom.xml b/bundles/pom.xml index 0f898fb54ee..0054a7b97a5 100644 --- a/bundles/pom.xml +++ b/bundles/pom.xml @@ -250,6 +250,7 @@ org.openhab.binding.luxtronikheatpump org.openhab.binding.magentatv org.openhab.binding.mail + org.openhab.binding.matter org.openhab.binding.max org.openhab.binding.mcd org.openhab.binding.mcp23017