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