2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.omnilink.internal.handler;
15 import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
17 import java.util.List;
19 import java.util.Optional;
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequest;
24 import org.openhab.binding.omnilink.internal.discovery.ObjectPropertyRequests;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.PercentType;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.types.Command;
33 import org.openhab.core.types.RefreshType;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import com.digitaldan.jomnilinkII.Message;
38 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
39 import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
40 import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties;
41 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
42 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SwitchPressEvent;
43 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
44 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
47 * The {@link AbstractOmnilinkHandler} defines some methods that can be used across
48 * the many different Units exposed by the OmniLink protocol
50 * @author Craig Hamilton - Initial contribution
51 * @author Ethan Dye - openHAB3 rewrite
54 public class UnitHandler extends AbstractOmnilinkStatusHandler<ExtendedUnitStatus> {
55 private final Logger logger = LoggerFactory.getLogger(UnitHandler.class);
56 private final int thingID = getThingNumber();
57 public @Nullable String number;
59 public UnitHandler(Thing thing) {
64 public void initialize() {
66 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
67 if (bridgeHandler != null) {
68 updateUnitProperties(bridgeHandler);
70 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
71 "Received null bridge while initializing Unit!");
75 private void updateUnitProperties(OmnilinkBridgeHandler bridgeHandler) {
76 final List<AreaProperties> areas = getAreaProperties();
78 for (AreaProperties areaProperties : areas) {
79 int areaFilter = bitFilterForArea(areaProperties);
81 ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
82 .builder(bridgeHandler, ObjectPropertyRequests.UNIT, thingID, 0).selectNamed()
83 .areaFilter(areaFilter).selectAnyLoad().build();
85 for (UnitProperties unitProperties : objectPropertyRequest) {
86 Map<String, String> properties = editProperties();
87 properties.put(THING_PROPERTIES_NAME, unitProperties.getName());
88 properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
89 updateProperties(properties);
96 public void handleCommand(ChannelUID channelUID, Command command) {
97 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
99 if (command instanceof RefreshType) {
100 retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
101 ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null staus update!"));
105 switch (channelUID.getId()) {
106 case CHANNEL_UNIT_LEVEL:
107 case CHANNEL_UNIT_SWITCH:
108 if (command instanceof OnOffType) {
109 handleOnOff(channelUID, (OnOffType) command);
111 logger.debug("Invalid command: {}, must be OnOffType", command);
114 case CHANNEL_UNIT_ON_FOR_SECONDS:
115 case CHANNEL_UNIT_OFF_FOR_SECONDS:
116 case CHANNEL_UNIT_ON_FOR_MINUTES:
117 case CHANNEL_UNIT_OFF_FOR_MINUTES:
118 case CHANNEL_UNIT_ON_FOR_HOURS:
119 case CHANNEL_UNIT_OFF_FOR_HOURS:
120 if (command instanceof DecimalType) {
121 handleUnitDuration(channelUID, (DecimalType) command);
123 logger.debug("Invalid command: {}, must be DecimalType", command);
127 logger.warn("Unknown channel for Unit thing: {}", channelUID);
131 private void handleUnitDuration(ChannelUID channelUID, DecimalType command) {
132 logger.debug("handleUnitDuration called for channel: {}, command: {}", channelUID, command);
133 final String channelID = channelUID.getId();
136 if (channelID.endsWith("seconds")) {
137 duration = command.intValue();
138 } else if (channelID.endsWith("minutes")) {
139 duration = command.intValue() + 100;
140 } else if (channelID.endsWith("hours")) {
141 duration = command.intValue() + 200;
143 logger.warn("Unknown channel for Unit duration: {}", channelUID);
148 channelID.startsWith("on") ? OmniLinkCmd.CMD_UNIT_ON.getNumber() : OmniLinkCmd.CMD_UNIT_OFF.getNumber(),
152 protected void handleOnOff(ChannelUID channelUID, OnOffType command) {
153 logger.debug("handleOnOff called for channel: {}, command: {}", channelUID, command);
154 sendOmnilinkCommand(OnOffType.ON.equals(command) ? OmniLinkCmd.CMD_UNIT_ON.getNumber()
155 : OmniLinkCmd.CMD_UNIT_OFF.getNumber(), 0, thingID);
159 public void updateChannels(ExtendedUnitStatus status) {
160 logger.debug("updateChannels called for Unit status: {}", status);
161 int unitStatus = status.getStatus();
164 if (unitStatus == Status.UNIT_OFF) {
166 } else if (unitStatus == Status.UNIT_ON) {
168 } else if ((unitStatus >= Status.UNIT_SCENE_A) && (unitStatus <= Status.UNIT_SCENE_L)) {
170 } else if ((unitStatus >= Status.UNIT_LEVEL_0) && (unitStatus <= Status.UNIT_LEVEL_100)) {
171 level = unitStatus - Status.UNIT_LEVEL_0;
174 updateState(CHANNEL_UNIT_LEVEL, PercentType.valueOf(Integer.toString(level)));
175 updateState(CHANNEL_UNIT_SWITCH, OnOffType.from(level != 0));
179 protected Optional<ExtendedUnitStatus> retrieveStatus() {
181 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
182 if (bridgeHandler != null) {
183 ObjectStatus objectStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_UNIT, thingID, thingID,
185 return Optional.of((ExtendedUnitStatus) objectStatus.getStatuses()[0]);
187 logger.debug("Received null bridge while updating Unit status!");
188 return Optional.empty();
190 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
191 logger.debug("Received exception while refreshing Unit status: {}", e.getMessage());
192 return Optional.empty();
196 private static class Status {
197 private static final int UNIT_OFF = 0;
198 private static final int UNIT_ON = 1;
199 private static final int UNIT_SCENE_A = 2;
200 private static final int UNIT_SCENE_L = 13;
201 private static final int UNIT_LEVEL_0 = 100;
202 private static final int UNIT_LEVEL_100 = 200;
206 * Handle a switch press event by triggering the appropriate channel.
208 * @param switchPressEvent
210 public void handleSwitchPressEvent(SwitchPressEvent switchPressEvent) {
211 ChannelUID activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_SWITCH_PRESS_EVENT);
212 triggerChannel(activateChannel, Integer.toString(switchPressEvent.getSwitchValue()));