2 * Copyright (c) 2010-2023 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.binding.omnilink.internal.exceptions.BridgeOfflineException;
26 import org.openhab.core.library.types.DecimalType;
27 import org.openhab.core.library.types.OnOffType;
28 import org.openhab.core.library.types.PercentType;
29 import org.openhab.core.thing.ChannelUID;
30 import org.openhab.core.thing.Thing;
31 import org.openhab.core.thing.ThingStatus;
32 import org.openhab.core.thing.ThingStatusDetail;
33 import org.openhab.core.types.Command;
34 import org.openhab.core.types.RefreshType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import com.digitaldan.jomnilinkII.Message;
39 import com.digitaldan.jomnilinkII.MessageTypes.CommandMessage;
40 import com.digitaldan.jomnilinkII.MessageTypes.ObjectStatus;
41 import com.digitaldan.jomnilinkII.MessageTypes.properties.AreaProperties;
42 import com.digitaldan.jomnilinkII.MessageTypes.properties.UnitProperties;
43 import com.digitaldan.jomnilinkII.MessageTypes.statuses.ExtendedUnitStatus;
44 import com.digitaldan.jomnilinkII.MessageTypes.systemevents.SwitchPressEvent;
45 import com.digitaldan.jomnilinkII.OmniInvalidResponseException;
46 import com.digitaldan.jomnilinkII.OmniUnknownMessageTypeException;
49 * The {@link AbstractOmnilinkHandler} defines some methods that can be used across
50 * the many different Units exposed by the OmniLink protocol
52 * @author Craig Hamilton - Initial contribution
53 * @author Ethan Dye - openHAB3 rewrite
56 public class UnitHandler extends AbstractOmnilinkStatusHandler<ExtendedUnitStatus> {
57 private final Logger logger = LoggerFactory.getLogger(UnitHandler.class);
58 private final int thingID = getThingNumber();
59 public @Nullable String number;
61 public UnitHandler(Thing thing) {
66 public void initialize() {
68 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
69 if (bridgeHandler != null) {
70 updateUnitProperties(bridgeHandler);
72 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
73 "Received null bridge while initializing Unit!");
77 private void updateUnitProperties(OmnilinkBridgeHandler bridgeHandler) {
78 final List<AreaProperties> areas = getAreaProperties();
80 for (AreaProperties areaProperties : areas) {
81 int areaFilter = bitFilterForArea(areaProperties);
83 ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
84 .builder(bridgeHandler, ObjectPropertyRequests.UNIT, thingID, 0).selectNamed()
85 .areaFilter(areaFilter).selectAnyLoad().build();
87 for (UnitProperties unitProperties : objectPropertyRequest) {
88 Map<String, String> properties = editProperties();
89 properties.put(THING_PROPERTIES_NAME, unitProperties.getName());
90 properties.put(THING_PROPERTIES_AREA, Integer.toString(areaProperties.getNumber()));
91 updateProperties(properties);
98 public void handleCommand(ChannelUID channelUID, Command command) {
99 logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
101 if (command instanceof RefreshType) {
102 retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
103 ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null status update!"));
107 switch (channelUID.getId()) {
108 case CHANNEL_UNIT_LEVEL:
109 case CHANNEL_UNIT_SWITCH:
110 if (command instanceof OnOffType onOffCommand) {
111 handleOnOff(channelUID, onOffCommand);
113 logger.debug("Invalid command: {}, must be OnOffType", command);
116 case CHANNEL_UNIT_ON_FOR_SECONDS:
117 case CHANNEL_UNIT_OFF_FOR_SECONDS:
118 case CHANNEL_UNIT_ON_FOR_MINUTES:
119 case CHANNEL_UNIT_OFF_FOR_MINUTES:
120 case CHANNEL_UNIT_ON_FOR_HOURS:
121 case CHANNEL_UNIT_OFF_FOR_HOURS:
122 if (command instanceof DecimalType decimalCommand) {
123 handleUnitDuration(channelUID, decimalCommand);
125 logger.debug("Invalid command: {}, must be DecimalType", command);
129 logger.warn("Unknown channel for Unit thing: {}", channelUID);
133 private void handleUnitDuration(ChannelUID channelUID, DecimalType command) {
134 logger.debug("handleUnitDuration called for channel: {}, command: {}", channelUID, command);
135 final String channelID = channelUID.getId();
138 if (channelID.endsWith("seconds")) {
139 duration = command.intValue();
140 } else if (channelID.endsWith("minutes")) {
141 duration = command.intValue() + 100;
142 } else if (channelID.endsWith("hours")) {
143 duration = command.intValue() + 200;
145 logger.warn("Unknown channel for Unit duration: {}", channelUID);
149 sendOmnilinkCommand(channelID.startsWith("on") ? CommandMessage.CMD_UNIT_ON : CommandMessage.CMD_UNIT_OFF,
153 protected void handleOnOff(ChannelUID channelUID, OnOffType command) {
154 logger.debug("handleOnOff called for channel: {}, command: {}", channelUID, command);
155 sendOmnilinkCommand(OnOffType.ON.equals(command) ? CommandMessage.CMD_UNIT_ON : CommandMessage.CMD_UNIT_OFF, 0,
160 public void updateChannels(ExtendedUnitStatus status) {
161 logger.debug("updateChannels called for Unit status: {}", status);
162 int unitStatus = status.getStatus();
165 if (unitStatus == Status.UNIT_OFF) {
167 } else if (unitStatus == Status.UNIT_ON) {
169 } else if ((unitStatus >= Status.UNIT_SCENE_A) && (unitStatus <= Status.UNIT_SCENE_L)) {
171 } else if ((unitStatus >= Status.UNIT_LEVEL_0) && (unitStatus <= Status.UNIT_LEVEL_100)) {
172 level = unitStatus - Status.UNIT_LEVEL_0;
175 updateState(CHANNEL_UNIT_LEVEL, PercentType.valueOf(Integer.toString(level)));
176 updateState(CHANNEL_UNIT_SWITCH, OnOffType.from(level != 0));
180 protected Optional<ExtendedUnitStatus> retrieveStatus() {
182 final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
183 if (bridgeHandler != null) {
184 ObjectStatus objectStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_UNIT, thingID, thingID,
186 return Optional.of((ExtendedUnitStatus) objectStatus.getStatuses()[0]);
188 logger.debug("Received null bridge while updating Unit status!");
189 return Optional.empty();
191 } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
192 logger.debug("Received exception while refreshing Unit status: {}", e.getMessage());
193 return Optional.empty();
197 private static class Status {
198 private static final int UNIT_OFF = 0;
199 private static final int UNIT_ON = 1;
200 private static final int UNIT_SCENE_A = 2;
201 private static final int UNIT_SCENE_L = 13;
202 private static final int UNIT_LEVEL_0 = 100;
203 private static final int UNIT_LEVEL_100 = 200;
207 * Handle a switch press event by triggering the appropriate channel.
209 * @param switchPressEvent
211 public void handleSwitchPressEvent(SwitchPressEvent switchPressEvent) {
212 ChannelUID activateChannel = new ChannelUID(getThing().getUID(), TRIGGER_CHANNEL_SWITCH_PRESS_EVENT);
213 triggerChannel(activateChannel, Integer.toString(switchPressEvent.getSwitchValue()));