]> git.basschouten.com Git - openhab-addons.git/blob
6113103a716de55cfa96516e3b9ec107c5da6663
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 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.lutron.internal.handler;
14
15 import static org.openhab.binding.lutron.internal.LutronBindingConstants.*;
16
17 import java.time.ZoneId;
18 import java.time.ZonedDateTime;
19 import java.util.Calendar;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.lutron.internal.protocol.LutronCommandType;
24 import org.openhab.core.library.types.DateTimeType;
25 import org.openhab.core.library.types.DecimalType;
26 import org.openhab.core.thing.Bridge;
27 import org.openhab.core.thing.ChannelUID;
28 import org.openhab.core.thing.Thing;
29 import org.openhab.core.thing.ThingStatus;
30 import org.openhab.core.thing.ThingStatusDetail;
31 import org.openhab.core.types.Command;
32 import org.openhab.core.types.RefreshType;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * Handler responsible for communicating with the RA2 time clock.
38  *
39  * @author Bob Adair - Initial contribution
40  */
41 @NonNullByDefault
42 public class TimeclockHandler extends LutronHandler {
43     private static final Integer ACTION_CLOCKMODE = 1;
44     private static final Integer ACTION_SUNRISE = 2;
45     private static final Integer ACTION_SUNSET = 3;
46     private static final Integer ACTION_EXECEVENT = 5;
47     private static final Integer ACTION_SETEVENT = 6;
48     private static final Integer EVENT_ENABLE = 1;
49     private static final Integer EVENT_DISABLE = 2;
50
51     private final Logger logger = LoggerFactory.getLogger(TimeclockHandler.class);
52
53     private int integrationId;
54
55     public TimeclockHandler(Thing thing) {
56         super(thing);
57     }
58
59     @Override
60     public int getIntegrationId() {
61         return integrationId;
62     }
63
64     @Override
65     public void initialize() {
66         Number id = (Number) getThing().getConfiguration().get("integrationId");
67         logger.debug("Initializing timeclock handler");
68         if (id == null) {
69             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No integrationId");
70             return;
71         }
72         integrationId = id.intValue();
73         initDeviceState();
74     }
75
76     @Override
77     protected void initDeviceState() {
78         logger.debug("Initializing device state for Timeclock {}", getIntegrationId());
79         Bridge bridge = getBridge();
80         if (bridge == null) {
81             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "No bridge configured");
82         } else if (bridge.getStatus() == ThingStatus.ONLINE) {
83             updateStatus(ThingStatus.UNKNOWN, ThingStatusDetail.NONE, "Awaiting initial response");
84             queryTimeclock(ACTION_CLOCKMODE); // handleUpdate() will set thing status to online when response arrives
85         } else {
86             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
87         }
88     }
89
90     @Override
91     public void channelLinked(ChannelUID channelUID) {
92         logger.debug("Handling channel link request for timeclock {}", integrationId);
93         if (channelUID.getId().equals(CHANNEL_CLOCKMODE)) {
94             queryTimeclock(ACTION_CLOCKMODE);
95         }
96     }
97
98     @Override
99     public void handleCommand(ChannelUID channelUID, Command command) {
100         String channelID = channelUID.getId();
101         logger.debug("Handling timeclock command {} on channel {}", command, channelID);
102
103         if (channelUID.getId().equals(CHANNEL_CLOCKMODE)) {
104             if (command instanceof DecimalType) {
105                 Integer mode = ((DecimalType) command).intValue();
106                 timeclock(ACTION_CLOCKMODE, mode);
107             } else if (command instanceof RefreshType) {
108                 queryTimeclock(ACTION_CLOCKMODE);
109             } else {
110                 logger.debug("Invalid command type for clockmode channnel");
111             }
112         } else if (channelUID.getId().equals(CHANNEL_EXECEVENT)) {
113             if (command instanceof DecimalType) {
114                 Integer index = ((DecimalType) command).intValue();
115                 timeclock(ACTION_EXECEVENT, index);
116             } else {
117                 logger.debug("Invalid command type for execevent channnel");
118             }
119         } else if (channelUID.getId().equals(CHANNEL_SUNRISE)) {
120             if (command instanceof RefreshType) {
121                 queryTimeclock(ACTION_SUNRISE);
122             } else {
123                 logger.debug("Invalid command type for sunrise channnel");
124             }
125         } else if (channelUID.getId().equals(CHANNEL_SUNSET)) {
126             if (command instanceof RefreshType) {
127                 queryTimeclock(ACTION_SUNSET);
128             } else {
129                 logger.debug("Invalid command type for sunset channnel");
130             }
131         } else if (channelUID.getId().equals(CHANNEL_ENABLEEVENT)) {
132             if (command instanceof DecimalType) {
133                 Integer index = ((DecimalType) command).intValue();
134                 timeclock(ACTION_SETEVENT, index, EVENT_ENABLE);
135             } else {
136                 logger.debug("Invalid command type for enableevent channnel");
137             }
138         } else if (channelUID.getId().equals(CHANNEL_DISABLEEVENT)) {
139             if (command instanceof DecimalType) {
140                 Integer index = ((DecimalType) command).intValue();
141                 timeclock(ACTION_SETEVENT, index, EVENT_DISABLE);
142             } else {
143                 logger.debug("Invalid command type for disableevent channnel");
144             }
145         } else {
146             logger.debug("Command received on invalid channel");
147         }
148     }
149
150     private @Nullable Calendar parseLutronTime(final String timeString) {
151         Integer hour, minute;
152         Calendar calendar = Calendar.getInstance();
153         try {
154             String hh = timeString.split(":", 2)[0];
155             String mm = timeString.split(":", 2)[1];
156             hour = Integer.parseInt(hh);
157             minute = Integer.parseInt(mm);
158         } catch (NumberFormatException | IndexOutOfBoundsException exception) {
159             logger.warn("Invaid time format received from timeclock {}", integrationId);
160             return null;
161         }
162         calendar.set(Calendar.HOUR_OF_DAY, hour);
163         calendar.set(Calendar.MINUTE, minute);
164         return calendar;
165     }
166
167     @Override
168     public void handleUpdate(LutronCommandType type, String... parameters) {
169         if (type != LutronCommandType.TIMECLOCK) {
170             return;
171         }
172         logger.debug("Handling update received from timeclock {}", integrationId);
173
174         try {
175             if (parameters.length >= 2 && ACTION_CLOCKMODE.toString().equals(parameters[0])) {
176                 Integer mode = Integer.valueOf(parameters[1]);
177                 if (getThing().getStatus() == ThingStatus.UNKNOWN) {
178                     updateStatus(ThingStatus.ONLINE);
179                 }
180                 updateState(CHANNEL_CLOCKMODE, new DecimalType(mode));
181
182             } else if (parameters.length >= 2 && ACTION_SUNRISE.toString().equals(parameters[0])) {
183                 Calendar calendar = parseLutronTime(parameters[1]);
184                 if (calendar != null) {
185                     updateState(CHANNEL_SUNRISE,
186                             new DateTimeType(ZonedDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault())));
187                 }
188
189             } else if (parameters.length >= 2 && ACTION_SUNSET.toString().equals(parameters[0])) {
190                 Calendar calendar = parseLutronTime(parameters[1]);
191                 if (calendar != null) {
192                     updateState(CHANNEL_SUNSET,
193                             new DateTimeType(ZonedDateTime.ofInstant(calendar.toInstant(), ZoneId.systemDefault())));
194                 }
195
196             } else if (parameters.length >= 2 && ACTION_EXECEVENT.toString().equals(parameters[0])) {
197                 Integer index = Integer.valueOf(parameters[1]);
198                 updateState(CHANNEL_EXECEVENT, new DecimalType(index));
199
200             } else if (parameters.length >= 3 && ACTION_SETEVENT.toString().equals(parameters[0])) {
201                 Integer index = Integer.valueOf(parameters[1]);
202                 Integer state = Integer.valueOf(parameters[2]);
203                 if (state.equals(EVENT_ENABLE)) {
204                     updateState(CHANNEL_ENABLEEVENT, new DecimalType(index));
205                 } else if (state.equals(EVENT_DISABLE)) {
206                     updateState(CHANNEL_DISABLEEVENT, new DecimalType(index));
207                 }
208             }
209         } catch (NumberFormatException e) {
210             logger.debug("Encountered number format exception while handling update for timeclock {}", integrationId);
211             return;
212         }
213     }
214 }