Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[deconz] Add Pairing/Scene actions, new devices and improve code #14622

Merged
merged 10 commits into from Mar 18, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
223 changes: 138 additions & 85 deletions bundles/org.openhab.binding.deconz/README.md

Large diffs are not rendered by default.

Expand Up @@ -15,6 +15,7 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.thing.ThingTypeUID;
import org.openhab.core.thing.type.ChannelTypeUID;

/**
* The {@link BindingConstants} class defines common constants, which are
Expand Down Expand Up @@ -50,6 +51,8 @@ public class BindingConstants {
public static final ThingTypeUID THING_TYPE_CARBONMONOXIDE_SENSOR = new ThingTypeUID(BINDING_ID,
"carbonmonoxidesensor");
public static final ThingTypeUID THING_TYPE_AIRQUALITY_SENSOR = new ThingTypeUID(BINDING_ID, "airqualitysensor");
public static final ThingTypeUID THING_TYPE_MOISTURE_SENSOR = new ThingTypeUID(BINDING_ID, "moisturesensor");

// Special sensor - Thermostat
public static final ThingTypeUID THING_TYPE_THERMOSTAT = new ThingTypeUID(BINDING_ID, "thermostat");

Expand All @@ -75,6 +78,7 @@ public class BindingConstants {
public static final String CHANNEL_LAST_SEEN = "last_seen";
public static final String CHANNEL_POWER = "power";
public static final String CHANNEL_CONSUMPTION = "consumption";
public static final String CHANNEL_CONSUMPTION_2 = "consumption2";
public static final String CHANNEL_VOLTAGE = "voltage";
public static final String CHANNEL_CURRENT = "current";
public static final String CHANNEL_VALUE = "value";
Expand All @@ -101,11 +105,14 @@ public class BindingConstants {
public static final String CHANNEL_CARBONMONOXIDE = "carbonmonoxide";
public static final String CHANNEL_AIRQUALITY = "airquality";
public static final String CHANNEL_AIRQUALITYPPB = "airqualityppb";
public static final String CHANNEL_MOISTURE = "moisture";
public static final String CHANNEL_HEATSETPOINT = "heatsetpoint";
public static final String CHANNEL_THERMOSTAT_MODE = "mode";
public static final String CHANNEL_THERMOSTAT_LOCKED = "locked";
public static final String CHANNEL_TEMPERATURE_OFFSET = "offset";
public static final String CHANNEL_VALVE_POSITION = "valve";
public static final String CHANNEL_WINDOWOPEN = "windowopen";
public static final String CHANNEL_WINDOW_OPEN = "windowopen";
public static final String CHANNEL_EXTERNAL_WINDOW_OPEN = "externalwindowopen";

// group + light channel ids
public static final String CHANNEL_SWITCH = "switch";
Expand All @@ -122,6 +129,11 @@ public class BindingConstants {
public static final String CHANNEL_SCENE = "scene";
public static final String CHANNEL_ONTIME = "ontime";

// channel uids
public static final ChannelTypeUID CHANNEL_EFFECT_TYPE_UID = new ChannelTypeUID(BINDING_ID, CHANNEL_EFFECT);
public static final ChannelTypeUID CHANNEL_EFFECT_SPEED_TYPE_UID = new ChannelTypeUID(BINDING_ID,
CHANNEL_EFFECT_SPEED);

// Thing configuration
public static final String CONFIG_HOST = "host";
public static final String CONFIG_HTTP_PORT = "httpPort";
Expand Down
Expand Up @@ -13,10 +13,15 @@
package org.openhab.binding.deconz.internal;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.events.EventPublisher;
import org.openhab.core.thing.ThingUID;
import org.openhab.core.thing.binding.BaseDynamicCommandDescriptionProvider;
import org.openhab.core.thing.i18n.ChannelTypeI18nLocalizationService;
import org.openhab.core.thing.link.ItemChannelLinkRegistry;
import org.openhab.core.thing.type.DynamicCommandDescriptionProvider;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -29,14 +34,24 @@
@NonNullByDefault
@Component(service = { DynamicCommandDescriptionProvider.class, DeconzDynamicCommandDescriptionProvider.class })
public class DeconzDynamicCommandDescriptionProvider extends BaseDynamicCommandDescriptionProvider {

@Activate
public DeconzDynamicCommandDescriptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}

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

/**
* remove all descriptions for a given thing
*
* @param thingUID the thing's UID
*/
public void removeDescriptionsForThing(ThingUID thingUID) {
public void removeCommandDescriptionForThing(ThingUID thingUID) {
logger.trace("removing state description for thing {}", thingUID);
channelOptionsMap.entrySet().removeIf(entry -> entry.getKey().getThingUID().equals(thingUID));
}
Expand Down
Expand Up @@ -19,16 +19,20 @@

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.ThingUID;
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.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -45,6 +49,15 @@ public class DeconzDynamicStateDescriptionProvider extends BaseDynamicStateDescr

private final Map<ChannelUID, StateDescriptionFragment> stateDescriptionFragments = new ConcurrentHashMap<>();

@Activate
public DeconzDynamicStateDescriptionProvider(final @Reference EventPublisher eventPublisher, //
final @Reference ItemChannelLinkRegistry itemChannelLinkRegistry, //
final @Reference ChannelTypeI18nLocalizationService channelTypeI18nLocalizationService) {
this.eventPublisher = eventPublisher;
this.itemChannelLinkRegistry = itemChannelLinkRegistry;
this.channelTypeI18nLocalizationService = channelTypeI18nLocalizationService;
}

/**
* Set a state description for a channel. This description will be used when preparing the channel state by
* the framework for presentation. A previous description, if existed, will be replaced.
Expand All @@ -59,9 +72,10 @@ public void setDescriptionFragment(ChannelUID channelUID, StateDescriptionFragme
if (!stateDescriptionFragment.equals(oldStateDescriptionFragment)) {
logger.trace("adding state description for channel {}", channelUID);
stateDescriptionFragments.put(channelUID, stateDescriptionFragment);
ItemChannelLinkRegistry itemChannelLinkRegistry = this.itemChannelLinkRegistry;
ItemChannelLinkRegistry localItemChannelLinkRegistry = itemChannelLinkRegistry;
postEvent(ThingEventFactory.createChannelDescriptionChangedEvent(channelUID,
itemChannelLinkRegistry != null ? itemChannelLinkRegistry.getLinkedItemNames(channelUID) : Set.of(),
localItemChannelLinkRegistry != null ? localItemChannelLinkRegistry.getLinkedItemNames(channelUID)
: Set.of(),
stateDescriptionFragment, oldStateDescriptionFragment));
}
}
Expand Down
Expand Up @@ -19,9 +19,11 @@
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.PercentType;
Expand Down Expand Up @@ -67,7 +69,7 @@ public static int constrainToRange(int intValue, int min, int max) {
public static PercentType toPercentType(int val) {
int scaledValue = (int) Math.ceil(val / BRIGHTNESS_FACTOR);
return new PercentType(
Util.constrainToRange(scaledValue, PercentType.ZERO.intValue(), PercentType.HUNDRED.intValue()));
constrainToRange(scaledValue, PercentType.ZERO.intValue(), PercentType.HUNDRED.intValue()));
}

/**
Expand Down Expand Up @@ -95,4 +97,15 @@ public static DateTimeType convertTimestampToDateTime(String timestamp) {
ZoneOffset.UTC, ZoneId.systemDefault()));
}
}

/**
* get all keys corresponding to a given value of a map
J-N-K marked this conversation as resolved.
Show resolved Hide resolved
*
* @param map a map
* @param value the value to find in the map
* @return Stream of all keys for the value
*/
public static <@NonNull K, @NonNull V> Stream<K> getKeysFromValue(Map<K, V> map, V value) {
return map.entrySet().stream().filter(e -> e.getValue().equals(value)).map(Map.Entry::getKey);
}
}
@@ -0,0 +1,87 @@
/**
* Copyright (c) 2010-2023 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of 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.deconz.internal.action;

import java.util.Map;
import java.util.Objects;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.jetty.http.HttpMethod;
import org.openhab.binding.deconz.internal.Util;
import org.openhab.binding.deconz.internal.handler.DeconzBridgeHandler;
import org.openhab.core.automation.annotation.ActionInput;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link BridgeActions} provides actions for managing scenes in groups
*
* @author Jan N. Klug - Initial contribution
*/
@ThingActionsScope(name = "deconz")
@NonNullByDefault
public class BridgeActions implements ThingActions {
private final Logger logger = LoggerFactory.getLogger(BridgeActions.class);

private @Nullable DeconzBridgeHandler handler;

@RuleAction(label = "@text/action.permit-join-network.label", description = "@text/action.permit-join-network.description")
public void permitJoin(
@ActionInput(name = "duration", label = "@text/action.permit-join-network.duration.label", description = "@text/action.permit-join-network.duration.description") @Nullable Integer duration) {
DeconzBridgeHandler handler = this.handler;

if (handler == null) {
logger.warn("Deconz BridgeActions service ThingHandler is null!");
return;
}

int searchDuration = Util.constrainToRange(Objects.requireNonNullElse(duration, 120), 1, 240);

Object object = Map.of("permitjoin", searchDuration);
handler.sendObject("config", object, HttpMethod.PUT).thenAccept(v -> {
if (v.getResponseCode() != java.net.HttpURLConnection.HTTP_OK) {
logger.warn("Sending {} via PUT to config failed: {} - {}", object, v.getResponseCode(), v.getBody());
} else {
logger.trace("Result code={}, body={}", v.getResponseCode(), v.getBody());
logger.info("Enabled device searching for {} seconds on bridge {}.", searchDuration,
handler.getThing().getUID());
}
}).exceptionally(e -> {
logger.warn("Sending {} via PUT to config failed: {} - {}", object, e.getClass(), e.getMessage());
return null;
});
}

public static void permitJoin(ThingActions actions, @Nullable Integer duration) {
if (actions instanceof BridgeActions bridgeActions) {
bridgeActions.permitJoin(duration);
}
}

@Override
public void setThingHandler(@Nullable ThingHandler handler) {
if (handler instanceof DeconzBridgeHandler) {
this.handler = (DeconzBridgeHandler) handler;
}
}

@Override
public @Nullable ThingHandler getThingHandler() {
return handler;
}
}