]> git.basschouten.com Git - openhab-addons.git/blob
644601f7f5a681170e85e123797b0ed01dd95fac
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.omnilink.internal.handler;
14
15 import static org.openhab.binding.omnilink.internal.OmnilinkBindingConstants.*;
16
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Optional;
20
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;
37
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;
47
48 /**
49  * The {@link AbstractOmnilinkHandler} defines some methods that can be used across
50  * the many different Units exposed by the OmniLink protocol
51  *
52  * @author Craig Hamilton - Initial contribution
53  * @author Ethan Dye - openHAB3 rewrite
54  */
55 @NonNullByDefault
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;
60
61     public UnitHandler(Thing thing) {
62         super(thing);
63     }
64
65     @Override
66     public void initialize() {
67         super.initialize();
68         final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
69         if (bridgeHandler != null) {
70             updateUnitProperties(bridgeHandler);
71         } else {
72             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
73                     "Received null bridge while initializing Unit!");
74         }
75     }
76
77     private void updateUnitProperties(OmnilinkBridgeHandler bridgeHandler) {
78         final List<AreaProperties> areas = getAreaProperties();
79         if (areas != null) {
80             for (AreaProperties areaProperties : areas) {
81                 int areaFilter = bitFilterForArea(areaProperties);
82
83                 ObjectPropertyRequest<UnitProperties> objectPropertyRequest = ObjectPropertyRequest
84                         .builder(bridgeHandler, ObjectPropertyRequests.UNIT, thingID, 0).selectNamed()
85                         .areaFilter(areaFilter).selectAnyLoad().build();
86
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);
92                 }
93             }
94         }
95     }
96
97     @Override
98     public void handleCommand(ChannelUID channelUID, Command command) {
99         logger.debug("handleCommand called for channel: {}, command: {}", channelUID, command);
100
101         if (command instanceof RefreshType) {
102             retrieveStatus().ifPresentOrElse(this::updateChannels, () -> updateStatus(ThingStatus.OFFLINE,
103                     ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR, "Received null status update!"));
104             return;
105         }
106
107         switch (channelUID.getId()) {
108             case CHANNEL_UNIT_LEVEL:
109             case CHANNEL_UNIT_SWITCH:
110                 if (command instanceof OnOffType) {
111                     handleOnOff(channelUID, (OnOffType) command);
112                 } else {
113                     logger.debug("Invalid command: {}, must be OnOffType", command);
114                 }
115                 break;
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) {
123                     handleUnitDuration(channelUID, (DecimalType) command);
124                 } else {
125                     logger.debug("Invalid command: {}, must be DecimalType", command);
126                 }
127                 break;
128             default:
129                 logger.warn("Unknown channel for Unit thing: {}", channelUID);
130         }
131     }
132
133     private void handleUnitDuration(ChannelUID channelUID, DecimalType command) {
134         logger.debug("handleUnitDuration called for channel: {}, command: {}", channelUID, command);
135         final String channelID = channelUID.getId();
136
137         int duration;
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;
144         } else {
145             logger.warn("Unknown channel for Unit duration: {}", channelUID);
146             return;
147         }
148
149         sendOmnilinkCommand(channelID.startsWith("on") ? CommandMessage.CMD_UNIT_ON : CommandMessage.CMD_UNIT_OFF,
150                 duration, thingID);
151     }
152
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,
156                 thingID);
157     }
158
159     @Override
160     public void updateChannels(ExtendedUnitStatus status) {
161         logger.debug("updateChannels called for Unit status: {}", status);
162         int unitStatus = status.getStatus();
163         int level = 0;
164
165         if (unitStatus == Status.UNIT_OFF) {
166             level = 0;
167         } else if (unitStatus == Status.UNIT_ON) {
168             level = 100;
169         } else if ((unitStatus >= Status.UNIT_SCENE_A) && (unitStatus <= Status.UNIT_SCENE_L)) {
170             level = 100;
171         } else if ((unitStatus >= Status.UNIT_LEVEL_0) && (unitStatus <= Status.UNIT_LEVEL_100)) {
172             level = unitStatus - Status.UNIT_LEVEL_0;
173         }
174
175         updateState(CHANNEL_UNIT_LEVEL, PercentType.valueOf(Integer.toString(level)));
176         updateState(CHANNEL_UNIT_SWITCH, OnOffType.from(level != 0));
177     }
178
179     @Override
180     protected Optional<ExtendedUnitStatus> retrieveStatus() {
181         try {
182             final OmnilinkBridgeHandler bridgeHandler = getOmnilinkBridgeHandler();
183             if (bridgeHandler != null) {
184                 ObjectStatus objectStatus = bridgeHandler.requestObjectStatus(Message.OBJ_TYPE_UNIT, thingID, thingID,
185                         true);
186                 return Optional.of((ExtendedUnitStatus) objectStatus.getStatuses()[0]);
187             } else {
188                 logger.debug("Received null bridge while updating Unit status!");
189                 return Optional.empty();
190             }
191         } catch (OmniInvalidResponseException | OmniUnknownMessageTypeException | BridgeOfflineException e) {
192             logger.debug("Received exception while refreshing Unit status: {}", e.getMessage());
193             return Optional.empty();
194         }
195     }
196
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;
204     }
205
206     /**
207      * Handle a switch press event by triggering the appropriate channel.
208      *
209      * @param switchPressEvent
210      */
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()));
214     }
215 }