Skip to content

Commit

Permalink
Fixes after code review
Browse files Browse the repository at this point in the history
* Added English and Norwegian translation for error messages
* Added support for QuantityType for temperatures
* Added consistent usage of property constants
* Changed most logging to DEBUG or TRACE to avoid spamming logs
* Changed Override to OverridePlan to avoid import clash with java.lang.Import
* Lots of other small changes

Signed-off-by: Espen Fossen <espenaf@junta.no>
  • Loading branch information
espenaf committed Jul 17, 2022
1 parent 7c3847c commit 50ebd4b
Show file tree
Hide file tree
Showing 30 changed files with 475 additions and 260 deletions.
66 changes: 29 additions & 37 deletions bundles/org.openhab.binding.nobohub/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ This binding controls the Glen Dimplex Nobø Hub using the Nobø Hub API v1.1 th

![Nobo Hub](doc/nobohub.jpg)

It lets you read and change temperature and profile settings for zones, and read and set active overrides to change the
global mode of the hub.
It lets you read and change temperature and profile settings for zones, and read and set active overrides to change the global mode of the hub.

This binding is tested with the following devices:

Expand All @@ -26,8 +25,7 @@ Nobø Hub is the hub that communicates with switches and thermostats.

## Discovery

The hub will be automatically discovered. Before it can be used, you will have to update the configuration
with the last three digits of its serial number.
The hub will be automatically discovered. Before it can be used, you will have to update the configuration with the last three digits of its serial number.

When the hub is configured with the correct serial number, it will autodetect zones and components (thermostats and switches).

Expand All @@ -36,7 +34,7 @@ When the hub is configured with the correct serial number, it will autodetect zo
```
# Configuration for Nobø Hub
#
# Serial number of the Nobø hub to communicate with, 12 numbers.
# Serial number of the Nobø hub to communicate with, 12 digits.
serialNumber=103000xxxxxx
# Host name or IP address of the Nobø hub
Expand All @@ -53,22 +51,22 @@ hostName=10.0.0.10

### Zone

| channel | type | description |
|------------------------------|--------|--------------------------------------------|
| activeWeekProfileName | String | The name of the active week profile |
| activeWeekProfile | Number | The active week profile id |
| comfortTemperature | Number | The configured comfort temperature |
| ecoTemperature | Number | The configured eco temparature |
| currentTemperature | Number | The current temperature in the zone |
| calculatedWeekProfileStatus | String | The current override based on week profile |
| channel | type | description |
|------------------------------|--------------------|--------------------------------------------|
| activeWeekProfileName | String | The name of the active week profile |
| activeWeekProfile | Number | The active week profile id |
| comfortTemperature | Number:Temperature | The configured comfort temperature |
| ecoTemperature | Number:Temperature | The configured eco temparature |
| currentTemperature | Number:Temperature | The current temperature in the zone |
| calculatedWeekProfileStatus | String | The current override based on week profile |

CurrentTemperature only works if the zone has a device that reports it (e.g. a switch).

### Component

| channel | type | description |
|---------------------|--------|------------------------------------------|
| currentTemperature | Number | The current temperature of the component |
| channel | type | description |
|---------------------|--------------------|------------------------------------------|
| currentTemperature | Number:Temperature | The current temperature of the component |

Not all devices report this.

Expand All @@ -87,18 +85,18 @@ Bridge nobohub:nobohub:controller "Nobø Hub" [ hostName="192.168.1.10", serialN

```
// Hub
String Nobo_Hub_GlobalOverride "Global Override %s" <heating> {channel="nobohub:nobohub:controller:activeOverrideName"}
String Nobo_Hub_GlobalOverride "Global Override %s" <heating> {channel="nobohub:nobohub:controller:activeOverrideName"}
// Panel Heater
Number PanelHeater_CurrentTemperatur "Setpoint [%.1f °C]" <temperature> {channel="nobohub:component:controller:SERIAL_NUMBER_COMPONENT:currentTemperature"}
Number:Temperature PanelHeater_CurrentTemperatur "Setpoint [%.1f °C]" <temperature> {channel="nobohub:component:controller:SERIAL_NUMBER_COMPONENT:currentTemperature"}
// Zone
String Zone_ActiveWeekProfileName "Active week profile name [%s]" <calendar> {channel="nobohub:zone:controller:1:activeWeekProfileName"}
Number Zone_ActiveWeekProfile "Active week profile [%d]" <calendar> {channel="nobohub:zone:controller:1:activeWeekProfile"}
String Zone_ActiveStatus "Active status %s]" <heating> {channel="nobohub:zone:controller:1:calculatedWeekProfileStatus"}
Number Zone_ComfortTemperatur "Comfort temperature [%.1f °C]" <temperature> {channel="nobohub:zone:controller:1:comfortTemperature"}
Number Zone_EcoTemperatur "Eco temperature [%.1f °C]" <temperature> {channel="nobohub:zone:controller:1:ecoTemperature"}
Number Zone_CurrentTemperatur "Current temperature [%.1f °C]" <temperature> {channel="nobohub:zone:controller:1:currentTemperature"}
String Zone_ActiveWeekProfileName "Active week profile name [%s]" <calendar> {channel="nobohub:zone:controller:1:activeWeekProfileName"}
Number Zone_ActiveWeekProfile "Active week profile [%d]" <calendar> {channel="nobohub:zone:controller:1:activeWeekProfile"}
String Zone_ActiveStatus "Active status %s]" <heating> {channel="nobohub:zone:controller:1:calculatedWeekProfileStatus"}
Number:Temperature Zone_ComfortTemperatur "Comfort temperature [%.1f °C]" <temperature> {channel="nobohub:zone:controller:1:comfortTemperature"}
Number:Temperature Zone_EcoTemperatur "Eco temperature [%.1f °C]" <temperature> {channel="nobohub:zone:controller:1:ecoTemperature"}
Number:Temperature Zone_CurrentTemperatur "Current temperature [%.1f °C]" <temperature> {channel="nobohub:zone:controller:1:currentTemperature"}
```

### nobo.sitemap
Expand All @@ -125,15 +123,12 @@ sitemap nobo label="Nobø " {

## Organize your setup

Nobø Hub uses a combination of status types (Normal, Comfort, Eco, Away), profiles types (Comfort, Eco, Away, Off),
predfined temperature types (Comfort, Eco, Away), zones and override settings to organize and enable different features.
This makes it possible to control the heaters in many different scenarios and combinations. The following is a suggested
way of organizing the binding with the Hub for a good level of control and flexibility.
Nobø Hub uses a combination of status types (Normal, Comfort, Eco, Away), profiles types (Comfort, Eco, Away, Off), predefined temperature types (Comfort, Eco, Away), zones and override settings to organize and enable different features.
This makes it possible to control the heaters in many different scenarios and combinations. The following is a suggested way of organizing the binding with the Hub for a good level of control and flexibility.

If you own panels with a physical Comfort temperature override, you need to use the Eco temperature type for setting
level used by the day based profiles. If not, you can use either Comfort or Eco to set wanted leve.
If you own panels with a physical Comfort temperature override, you need to use the Eco temperature type for setting level used by the day based profiles. If not, you can use either Comfort or Eco to set wanted leve.

Start creating the following profiles in the Nobø Hub App:
Start by creating the following profiles in the Nobø Hub App:

OFF Set to status off all day, every day.
ON Set to status [Comfort|Eco] all day, every day
Expand All @@ -146,11 +141,9 @@ Start på creating the following profiles in the Nobø Hub App:
Every day 06->16 Set to status [Comfort|Eco] between 06->16 every day, otherwise set to [Away|Off]
Every day 06->23 Set to status [Comfort|Eco] between 06->23 every day, otherwise set to [Away|Off]

Next set [Comfort|Eco] level for each zone to your requirements. For a more advanced setup, you can create a rule which
both sets temperature level and profile.
Next set [Comfort|Eco] level for each zone to your requirements. For a more advanced setup, you can create a rule which both sets temperature level and profile.

Then create a sitemap with a Selection pointing to the Week Profile item. The binding will now automatically update all
available week profile options in the selection button:
Then create a sitemap with a Selection pointing to the Week Profile item. The binding will now automatically update all available week profile options in the selection button:

### nobo.sitemap

Expand All @@ -165,8 +158,7 @@ sitemap nobo label="Nobø " {

## Bugs and logging

If you find any bugs or unwanted behaviour, please contact the maintainer. To help the maintainer it would be great
if you could send logs with a description of what is wrong. To turn on logging, go to the Keraf console and run
If you find any bugs or unwanted behaviour, please contact the maintainer. To help the maintainer it would be great if you could send logs with a description of what is wrong. To turn on logging, go to the Keraf console and run

log:set DEBUG org.openhab.binding.nobohub

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@
package org.openhab.binding.nobohub.internal;

import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.CHANNEL_COMPONENT_CURRENT_TEMPERATURE;
import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_MODEL;
import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_NAME;
import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_TEMPERATURE_SENSOR_FOR_ZONE;
import static org.openhab.binding.nobohub.internal.NoboHubBindingConstants.PROPERTY_ZONE;

import javax.measure.quantity.Temperature;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.nobohub.internal.model.Component;
import org.openhab.binding.nobohub.internal.model.SerialNumber;
import org.openhab.binding.nobohub.internal.model.Zone;
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.Bridge;
import org.openhab.core.thing.ChannelUID;
import org.openhab.core.thing.Thing;
Expand All @@ -41,10 +48,13 @@ public class ComponentHandler extends BaseThingHandler {

private final Logger logger = LoggerFactory.getLogger(ComponentHandler.class);

private final NoboHubTranslationProvider messages;

protected @Nullable SerialNumber serialNumber;

public ComponentHandler(Thing thing) {
public ComponentHandler(Thing thing, NoboHubTranslationProvider messages) {
super(thing);
this.messages = messages;
}

public void onUpdate(Component component) {
Expand All @@ -53,25 +63,25 @@ public void onUpdate(Component component) {
double temp = component.getTemperature();
if (!Double.isNaN(temp)) {
try {
DecimalType currentTemperature = new DecimalType(temp);
QuantityType<Temperature> currentTemperature = new QuantityType<>(temp, SIUnits.CELSIUS);
updateState(CHANNEL_COMPONENT_CURRENT_TEMPERATURE, currentTemperature);
} catch (NumberFormatException nfe) {
logger.debug("Couldnt set decimal value to temperature: {}", temp);
logger.debug("Couldn't set decimal value to temperature: {}", temp);
}
}

updateProperty("serialNumber", component.getSerialNumber().toString());
updateProperty("name", component.getName());
updateProperty("model", component.getSerialNumber().getComponentType());
updateProperty(Thing.PROPERTY_SERIAL_NUMBER, component.getSerialNumber().toString());
updateProperty(PROPERTY_NAME, component.getName());
updateProperty(PROPERTY_MODEL, component.getSerialNumber().getComponentType());

String zoneName = getZoneName(component.getZoneId());
if (zoneName != null) {
updateProperty("zone", zoneName);
updateProperty(PROPERTY_ZONE, zoneName);
}

String tempForZoneName = getZoneName(component.getTemperatureSensorForZoneId());
if (tempForZoneName != null) {
updateProperty("temperatureSensorForZone", tempForZoneName);
updateProperty(PROPERTY_TEMPERATURE_SENSOR_FOR_ZONE, tempForZoneName);
}
}

Expand All @@ -93,17 +103,17 @@ public void onUpdate(Component component) {
@Override
public void initialize() {
String serialNumberString = getConfigAs(ComponentConfiguration.class).serialNumber;
if (serialNumberString != null) {
if (serialNumberString != null && !serialNumberString.isEmpty()) {
SerialNumber sn = new SerialNumber(serialNumberString);
if (!sn.isWellFormed()) {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR,
"Illegal serial number: " + serialNumber);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
"@text/message.component.illegal.serial [\"" + serialNumber + "\"]");
} else {
this.serialNumber = sn;
updateStatus(ThingStatus.ONLINE);
}
} else {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.CONFIGURATION_ERROR, "Missing serial number");
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "@text/message.missing.serial");
}
}

Expand All @@ -114,15 +124,14 @@ public void handleCommand(ChannelUID channelUID, Command command) {
if (null != serialNumber) {
Component component = getComponent();
if (null == component) {
logger.error("Could not find Component with serial number {} for channel {}", serialNumber,
channelUID);
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.GONE);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE,
messages.getText("message.component.notfound", serialNumber, channelUID));
} else {
onUpdate(component);
}
} else {
updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.GONE);
logger.error("id not set for channel {}", channelUID);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.GONE,
"@text/message.component.missing.id [\"" + channelUID + "\"]");
}

return;
Expand All @@ -140,7 +149,7 @@ public void handleCommand(ChannelUID channelUID, Command command) {
if (null != noboHub) {
NoboHubBridgeHandler hubHandler = (NoboHubBridgeHandler) noboHub.getHandler();
if (null != serialNumber && null != hubHandler) {
SerialNumber sn = Helpers.castToNonNull(serialNumber, "serialNumber");
SerialNumber sn = Helpers.castToNonNull(serialNumber, Thing.PROPERTY_SERIAL_NUMBER);
NoboHubBridgeHandler hh = Helpers.castToNonNull(hubHandler, "hubHandler");
return hh.getComponent(sn);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,36 @@ public class NoboHubBindingConstants {
private static final String BINDING_ID = "nobohub";

public static final String API_VERSION = "1.1";
public static final String VENDOR = "Glen Dimplex Nobø";

public static final String PROPERTY_NAME = "name";
public static final String PROPERTY_MODEL = "model";
public static final String PROPERTY_HOSTNAME = "hostName";

public static final String PROPERTY_VENDOR_NAME = "Glen Dimplex Nobø";
public static final String PROPERTY_PRODUCTION_DATE = "productionDate";

public static final String PROPERTY_SOFTWARE_VERSION = "softwareVersion";

public static final String PROPERTY_ZONE = "zone";
public static final String PROPERTY_ZONE_ID = "id";
public static final String PROPERTY_TEMPERATURE_SENSOR_FOR_ZONE = "temperatureSensorForZone";

public static final int NOBO_HUB_TCP_PORT = 27779;

public static final Duration TIME_BETWEEN_FULL_SCANS = Duration.ofMinutes(10);
public static final Duration TIME_BETWEEN_RETRIES_ON_ERROR = Duration.ofSeconds(10);

public static final Duration RECOMMENDED_KEEPALIVE_INTERVAL = Duration.ofSeconds(14);

// List of all Thing Type UIDs
public static final ThingTypeUID THING_TYPE_HUB = new ThingTypeUID(BINDING_ID, "nobohub");
public static final ThingTypeUID THING_TYPE_ZONE = new ThingTypeUID(BINDING_ID, "zone");
public static final ThingTypeUID THING_TYPE_COMPONENT = new ThingTypeUID(BINDING_ID, "component");

public static final Set<ThingTypeUID> AUTODISCOVERED_THING_TYPES_UIDS = new HashSet<ThingTypeUID>(
public static final Set<ThingTypeUID> AUTODISCOVERED_THING_TYPES_UIDS = new HashSet<>(
Arrays.asList(THING_TYPE_ZONE, THING_TYPE_COMPONENT));

public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<ThingTypeUID>(
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = new HashSet<>(
Arrays.asList(THING_TYPE_HUB, THING_TYPE_ZONE, THING_TYPE_COMPONENT));

// List of all Channel ids
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
public class NoboHubBridgeConfiguration {

/**
* Serial Number of Nobø Hub.
* Serial number of Nobø Hub.
*/
@Nullable
public String serialNumber;
Expand Down
Loading

0 comments on commit 50ebd4b

Please sign in to comment.