Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 215 additions & 6 deletions developers/utils/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,207 @@ title: Framework Tools

# Framework Tools

Find a few framework utilities listed in this chapter.
The openHAB framework provides a number of Java tools and utilities so that addon developers do not have to _"re-invent the wheel"_ when writing their own addon code:

# Unique Instance ID
- [Channel Types](#channel-types)
- [Thing Properties](#thing-properties)
- [Hex Utilities](#hex-utilities)
- [Statistics Utilities](#statistics-utilities)
- [String Utilities](#string-utilities)
- [Lighting Model and Color Utilities](#lighting-model-and-color-utilities)
- [Unique ID Utilities](#unique-id-utilities)
- [Network Configuration Utilities](#network-address-service)
- [Caching Support](#caching)

## Channel Types

The openHAB framework provides many standard channel types.
These should be used wherever possible in preference to defining your own.

```java
ChannelType on = DefaultSystemChannelTypeProvider.SYSTEM_POWER;
```

## Thing Properties

The openHAB framework provides many standard property types.
These should be used wherever possible in preference to defining your own.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What advantages offer the standard property types, like Thing.PROPERTY_SERIAL_NUMBER? A disadvantage is that the property names are not translated depending on the Regional Settings or Browsers preferences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is documenting existing functionality. Only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR recommends to use the property names defined in the Thing interface, like Thing.PROPERTY_SERIAL_NUMBER. It does not just state that the Thing interface has some constants, the PR recommends using these constants.

So what advantage has using Thing.PROPERTY_SERIAL_NUMBER over some other string, which is translated by the binding, depending on user preferences?

I suggest removing the ## Thing Properties section.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properties are not supposed to be translated. In that way they are similar to channel ids. These system-wide global property names make it easy for example to create a list of current firmware versions for all devices. Or maybe for a widget to show specific device model.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not open a separate issue to discuss that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properties are not supposed to be translated. In that way they are similar to channel ids.

Channel ids are fixed, but the description of the channels can be translated, I do not think this is similar. The user is supposed to see the property names and property values (like “Has Bluetooth module: YES”), so in my eyes the property names should be translated. Theoretically this will not prevent the creation of a list of firmware versions of all devices in openHAB, just the user should see instead of “Firmware“ or “Serial Number” as property name translated text.

Besides in the openHAB documentation I am not aware of text discouraging the internationalization/localization of property names and values.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Properties should be camelCase, this is documented here: https://www.openhab.org/docs/developer/bindings/thing-xml.html#properties

From a technical discovery perspective, properties and configuration parameters are both provided as properties in camelCase and can also be defined statically in Thing XML definition. The I18N tool does not make these available for translation.

As @andrewfg proposed, please create an issue for continuing this discussion.

```java
String id = Thing.PROPERTY_SERIAL_NUMBER;
```

## Hex Utilities

The openHAB framework provides utilities for converting between hex encoded characters and byte arrays.

```java
String hexString = HexUtils.bytesToHex(byte[] bytes, @Nullable CharSequence delimiter);
byte[] bytes = HexUtils.hexToBytes(String hexString, String delimiter);
```

## Statistics Utilities

The openHAB framework provides some utilities for statistical functions.

```java
BigDecimal median = Statistics.median(List<BigDecimal> inputList);
```

## String Utilities

The openHAB framework provides some utilities for string manipulation functions.

```java
String title = StringUtils.capitalizeByWhitespace("openHAB is cool");
```

## Lighting Model and Color Utilities

The openHAB framework provides modelling capabilities and utilities for lighting Things.
The openHAB framework uses one single standard class `HSBType` for representing the state of light devices.
This class encapsulates the hue (H), saturation (S) and brightness (B) of the light; and depending on whether B is zero, therefore also its on/off state.
In addition a light device may have a color temperature (e.g. warm, cool) which represents a specific white-like color.

By contrast many (if indeed not most) actual lighting equipment does NOT use the HSB model.
Some equipment use the color X-Y model.
And others use led color models such as RGB (three color leds), RGBW (three color leds plus white), or even RGBCW (three color leds plus cool and warm white).

The openHAB framework provides tools for converting between the openHAB HSB model and these other actual equipment models.
There are two types of tools:

- A full featured "anything to anything" [Lighting Model](#lighting-model)
- Individual "point-to-point" [Color Conversion Utilities](#color-conversion-utilities)

### Lighting Model

The openHAB framework provides a full featured "anything to anything" state machine model for maintaining and modifying the state of a light.
It is intended to be used within the Thing Handler of a lighting binding.
It models the full physical state of a light device within Java code.
The state machine has two programming "sides":

1. The openHAB side, which "speaks the language of openHAB" using the standard openHAB `Command` and `State` classes such as `HSBType`, `OnOffType`, `PercentType` etc.
1. The physical device side, which "speaks the physical language of the equipment using models like `CIE XY`, `RGB`, `RGBW`, `RGBCW` etc.

It supports lights with different capabilities, including:

- On/Off only
- On/Off with Brightness
- On/Off with Brightness and Color Temperature
- On/Off with Brightness and Color (HSB, RGB, or CIE XY)
- On/Off with Brightness, Color Temperature, and Color

It maintains an internal representation of the state of the light.
It provides methods to handle commands from openHAB and to update the state from the remote light.
It also provides configuration methods to set the capabilities and parameters of the light.
The state machine maintains a consistent state, ensuring that the On/Off state is derived from the
brightness, and that the color temperature and color are only set if the capabilities are supported.
It also provides utility methods to convert between different color representations.

The model specifically handles the following "exotic" cases:

- It handles inter relationships between the brightness PercentType state, the 'B' part of the HSBType state, and the OnOffType state.
Where if the brightness goes below the configured `minimum on brightness level` the on/off state changes from ON to OFF, and the brightness is clamped to 0%.
And analogously if the on/off state changes from OFF to ON, the brightness changes from 0% to its last non zero value.
- It handles IncreaseDecreaseType commands to change the brightness up or down by the configured `stepSize`, and ensures that the brightness is clamped in the range [0%..100%].
- It handles both color temperature PercentType states and QuantityType states (which may be either in Mirek/Mired or Kelvin).
Where color temperature PercentType values are internally converted to Mirek/Mired values on the percentage scale between the configured cooleast and warmest Mirek/Mired values, and vice versa.
- When the color temperature changes then the HS values are adapted to match the corresponding color temperature point on the Planckian Locus in the CIE color chart.
- It handles input/output values in RGB format in the range [0..255].
The behavior depends on the `rgbDataType` setting.
- `DEFAULT`: the RGB values read/write all three parts of the HSBType state.
Whereas if it is `RGB_NO_BRIGHTNESS` the RGB values read/write only the 'HS' parts.
- `RGB_W`: it handles values in RGBW format.
The behavior is similar to the RGB case above except that the white channel is derived from the lowest of the RGB values.
- `RGB_C_W` it handles values in RGBCW format.
The behavior is similar to the `RGB_W` case above except that the white channel is derived from the RGB values by a custom algorithm.

```java
public class LightModelHandler extends BaseThingHandler {

// initialize the light model with default capabilities and parameters
private final LightModel model = new LightModel();

@Override
public void initialize() {
// Set up the light state machine capabilities.
model.configSetLightCapabilities(LightCapabilities.COLOR_WITH_COLOR_TEMPERATURE);

// Optionally: set up the light state machine configuration parameters.
// These would typically be read from the thing configuration or read from the remote device.
model.configSetRgbDataType(RgbDataType.RGB_NO_BRIGHTNESS); // RGB data type
model.configSetMinimumOnBrightness(2); // minimum brightness level when on 2%
model.configSetIncreaseDecreaseStep(10); // step size for increase/decrease commands
model.configSetMirekControlCoolest(153); // color temperature control range
model.configSetMirekControlWarmest(500); // color temperature control range

// Optionally: if the light has warm and cool white LEDS then set up their LED color temperatures.
// These would typically be read from the thing configuration or read from the remote device.
model.configSetMirekCoolWhiteLED(153);
model.configSetMirekWarmWhiteLED(500);

// now set the status to UNKNOWN to indicate that we are initialized
updateStatus(ThingStatus.UNKNOWN);
}

@Override
public void handleCommand(ChannelUID channelUID, Command command) {
// update the model state based on a command from OpenHAB
model.handleCommand(command);

// or if it is a color temperature command
model.handleColorTemperatureCommand(command);

sendBindingSpecificCommandToUpdateRemoteLight(
.. model.getOnOff() or
.. model.getBrightness() or
.. model.getColor() or
.. model.getColorTemperature() or
.. model.getColorTemperaturePercent() or
.. model.getRGBx() or
.. model.getXY() or
);
}

// method that sends the updated state data to the remote light
private void sendBindingSpecificCommandToUpdateRemoteLight(..) {
// binding specific code
}

// method that receives data from remote light, and updates the model, and then OH
private void receiveBindingSpecificDataFromRemoteLight(double... receivedData) {
// update the model state based on the data received from the remote
model.setBrightness(receivedData[0]);
model.setRGBx(receivedData[1], receivedData[2], receivedData[3]);
model.setMirek(receivedData[4]);

// update the OH channels with the new state values
updateState(onOffChannelUID, model.getOnOff());
updateState(brightnessChannelUID, model.getBrightness());
updateState(colorChannelUID, model.getColor());
updateState(colorTemperatureChannelUID, model.getColorTemperature());
}
}
```

### Color Conversion Utilities

The openHAB framework provides some "point to point" utilities for simpler color conversions.

```java
int[] rgb = hsbToRgb(HSBType hsb);
PercentType[] rgbPercent = hsbToRgbPercent(HSBType hsb);

double[] cieXY = hsbToXY(HSBType hsb);
HSBType hsb = xyToHsb(double[] xy);

double[] cieXY = kelvinToXY(double kelvin);
double kelvin = xyToKelvin(double[] xy);
```

## Unique ID Utilities

### Unique Instance ID Generator

When communicating with external systems, it is often desirable to have a unique identifier.
The `org.openhab.core.id` bundle is a mean to generate such an id, which is automatically persisted.
Expand All @@ -19,7 +217,18 @@ The id is provided through a static method and can be retrieved through
String uuid = InstanceUUID.get();
```

# Network Address Service
### UUID Encoder and Decoder

In openhHAB only certain characters are allowed in Thing and Channel UUIDs (Universally Unique Identifiers).
The UIDUtils allow to convert a string containing any characters to a string containing only the allowed characters.

```java
String uuid = UIDUtils.encode("anyString");

String anyString = UIDUtils.decode(uui);
```

## Network Address Service

The `NetworkAddressService` is an OSGi service that can be used like any other OSGi service by adding a service reference to it.
Its OSGi service name is `org.openhab.core.network`.
Expand All @@ -29,7 +238,7 @@ This service is useful for example in the `ThingHandlerFactory` or an `AudioSink

Some static methods like `getAllBroadcastAddresses()` for retrieving all interface broadcast addresses or `getInterfaceAddresses()` for retrieving all assigned interface addresses might be usefull as well for discovery services.

## Network Address Change Listener
### Network Address Change Listener

The `NetworkAddressChangeListener` is a consumer type OSGi service interface.
If listeners want to be notified about network interface address changes, they can implement `NetworkAddressChangeListener` and register as an OSGi service.
Expand All @@ -38,7 +247,7 @@ Please be aware that not all network interface changes are notified to the liste
When a network interface status changes from "up" to "down", it is considered as "removed".
When a "loopback" or "down" interface is added, the listeners are not notified.

# Caching
## Caching

The framework provides some caching solutions for common scenarios.

Expand All @@ -51,7 +260,7 @@ It is a good practice to return as fast as possible from the `handleCommand(Chan
Use this type of cache only, if your refresh action is a quick to compute, blocking operation.
If you deal with network calls, consider the asynchronously reloading cache implementation instead.

## Expiring and asynchronously reloading cache
### Expiring and asynchronously reloading cache

If we refreshed a value of the internal state in a `ThingHandler` just recently, we can return it immediately via the usual `updateState(channel, state)` method in response to a "RefreshType" command.
If the state is too old, we need to fetch it first and this may involve network calls, interprocess operations or anything else that will would block for a considerable amount of time.
Expand Down