]> git.basschouten.com Git - openhab-addons.git/blob
3a05518a7fb0800a880a9f62397ae6a565065079
[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.cm11a.internal.handler;
14
15 import java.util.List;
16
17 import org.openhab.binding.cm11a.internal.X10Interface;
18 import org.openhab.binding.cm11a.internal.X10ReceivedData;
19 import org.openhab.binding.cm11a.internal.X10ReceivedData.X10COMMAND;
20 import org.openhab.binding.cm11a.internal.config.Cm11aConfig;
21 import org.openhab.core.library.types.OnOffType;
22 import org.openhab.core.thing.Bridge;
23 import org.openhab.core.thing.Channel;
24 import org.openhab.core.thing.ChannelUID;
25 import org.openhab.core.thing.Thing;
26 import org.openhab.core.thing.ThingStatus;
27 import org.openhab.core.thing.ThingStatusDetail;
28 import org.openhab.core.thing.binding.BaseBridgeHandler;
29 import org.openhab.core.types.Command;
30 import org.openhab.core.types.State;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import gnu.io.NoSuchPortException;
35
36 /**
37  * The {@link Cm11aBridgeHandler} is the Bridge (see
38  * https://openhab.org/documentation/development/bindings/bridge-handler.html
39  * for a description of OpenHAB bridges. This gets called first and is responsible for setting up the handler. Mostly it
40  * loads the 10Interface class which does all of the heavy lifting.
41  *
42  * @author Bob Raker - Initial contribution
43  */
44 public class Cm11aBridgeHandler extends BaseBridgeHandler implements ReceivedDataListener {
45
46     private Cm11aConfig cm11aConfig;
47
48     private X10Interface x10Interface;
49
50     private Bridge bridge;
51
52     private final Logger logger = LoggerFactory.getLogger(Cm11aBridgeHandler.class);
53
54     public Cm11aBridgeHandler(Bridge bridge) {
55         super(bridge);
56         this.bridge = bridge;
57     }
58
59     public Cm11aConfig getCm11AConfig() {
60         return this.cm11aConfig;
61     }
62
63     @Override
64     public void handleCommand(ChannelUID channelUID, Command command) {
65         // Commands are handled by the "Things" which include Cm11aSwitchHandler and Cm11aLampHandler
66     }
67
68     @Override
69     public void initialize() {
70         // Get serial port number from config
71         cm11aConfig = getThing().getConfiguration().as(Cm11aConfig.class);
72         logger.trace("********* cm11a initialize started *********");
73
74         // Verify the configuration is valid
75         if (!validateConfig(this.cm11aConfig)) {
76             return;
77         }
78
79         // Initialize the X10 interface
80         try {
81             x10Interface = new X10Interface(cm11aConfig.serialPort, this);
82             x10Interface.setDaemon(true);
83             x10Interface.start();
84             x10Interface.addReceivedDataListener(this);
85             logger.info("Initialized CM11A X10 interface on: {}", cm11aConfig.serialPort);
86         } catch (NoSuchPortException e) {
87             x10Interface = null;
88             logger.error("No such port exists on this machine: {}", cm11aConfig.serialPort);
89             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
90                     "No such port exists on this machine: " + cm11aConfig.serialPort);
91             return;
92         }
93
94         updateStatus(ThingStatus.ONLINE);
95     }
96
97     @Override
98     public void dispose() {
99         // Close the serial port
100         if (x10Interface != null) {
101             x10Interface.disconnect();
102             x10Interface = null;
103         }
104         logger.debug("Cm11aBridgeHandler is being removed.");
105     }
106
107     /**
108      * Validate that the configuration is valid
109      *
110      * @param cm11aConfig2
111      * @return
112      */
113     private boolean validateConfig(Cm11aConfig config) {
114         if (config == null) {
115             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "cm11a configuration missing");
116             return false;
117         }
118
119         String port = config.serialPort;
120         if (port == null || port.isEmpty()) {
121             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "cm11a serialPort not specified");
122             return false;
123         }
124
125         return true;
126     }
127
128     /*
129      * (non-Javadoc)
130      *
131      * @see org.openhab.binding.cm11a.handler.ReceivedDataListener#receivedX10Data(org.openhab.binding.cm11a.internal.
132      * X10ReceivedData)
133      */
134     @Override
135     public void receivedX10Data(X10ReceivedData rd) {
136         logger.debug("Cm11aReceivedDataManager received the following data: {}", rd);
137
138         List<Thing> things = bridge.getThings();
139
140         // This block goes through the Things attached to this bridge and find the HouseUnitCode that matches what came
141         // from the serial port. Then it looks at the channels in that thing and looks for a channel that ends with
142         // "switchstatus" or "lightlevel"
143         // which is the one that should be updated.
144         synchronized (rd) {
145             for (Thing thing : things) {
146                 String houseUnitCode = (String) thing.getConfiguration().get("houseUnitCode");
147                 for (String messageHouseUnitCode : rd.getAddr()) {
148                     if (messageHouseUnitCode.equals(houseUnitCode)) {
149                         // The channel we want should end in "switchstatus" or "lightlevel". In reality there is
150                         // probably only one channel since these things only define one channel
151                         ChannelUID desiredChannelUid = findX10Channel(thing.getChannels());
152                         if (desiredChannelUid == null) {
153                             return;
154                         }
155                         X10COMMAND cmd = rd.getCmd();
156                         int dims = rd.getDims();
157                         updateX10State(thing, desiredChannelUid, cmd, dims);
158                     }
159                 }
160             }
161         }
162     }
163
164     /**
165      * Find the X10 channel.
166      *
167      * @param channels
168      * @return
169      */
170     private ChannelUID findX10Channel(List<Channel> channels) {
171         ChannelUID desiredChannelUid = null;
172         for (Channel channel : channels) {
173             if (channel.getUID().toString().endsWith("switchstatus")
174                     || channel.getUID().toString().endsWith("lightlevel")) {
175                 desiredChannelUid = channel.getUID();
176                 break;
177             }
178         }
179         return desiredChannelUid;
180     }
181
182     /**
183      * Update the X10 state
184      *
185      * @param thing
186      * @param channelUid
187      * @param cmd
188      * @param dims
189      */
190     private void updateX10State(Thing thing, ChannelUID channelUid, X10COMMAND cmd, int dims) {
191         if (thing == null) {
192             logger.debug("Unable to update X10 state: thing is null");
193             return;
194         }
195
196         Cm11aAbstractHandler handler = (Cm11aAbstractHandler) thing.getHandler();
197         if (handler == null) {
198             logger.debug("Unable to update X10 state: handler is null");
199             return;
200         }
201
202         // Perform appropriate update based on X10Command received
203         // Handle ON/OFF commands
204         if (cmd == X10ReceivedData.X10COMMAND.ON) {
205             updateState(channelUid, OnOffType.ON);
206             handler.setCurrentState(OnOffType.ON);
207         } else if (cmd == X10ReceivedData.X10COMMAND.OFF) {
208             updateState(channelUid, OnOffType.OFF);
209             handler.setCurrentState(OnOffType.OFF);
210             // Handle DIM/Bright commands
211         } else if (cmd == X10ReceivedData.X10COMMAND.DIM) {
212             State newState = handler.addDimsToCurrentState(dims);
213             updateState(channelUid, newState);
214             handler.setCurrentState(newState);
215             logger.debug("Current state set to: {}", handler.getCurrentState().toFullString());
216         } else if (cmd == X10ReceivedData.X10COMMAND.BRIGHT) {
217             State newState = handler.addBrightsToCurrentState(dims);
218             updateState(channelUid, newState);
219             handler.setCurrentState(newState);
220             logger.debug("Current state set to: {}", handler.getCurrentState().toFullString());
221         } else {
222             logger.warn("Received unknown command from cm11a: {}", cmd);
223         }
224     }
225
226     /**
227      * Get the X10Interface
228      *
229      * @return the X10Interface
230      */
231     public X10Interface getX10Interface() {
232         return x10Interface;
233     }
234
235     public void changeBridgeStatusToUp() {
236         if (getThing().getStatus().equals(ThingStatus.OFFLINE)) {
237             updateStatus(ThingStatus.ONLINE);
238             logger.debug("Changed the Bridge status to online because the serial interface is working again.");
239         }
240     }
241
242     public void changeBridgeStatusToDown(String message) {
243         if (getThing().getStatus().equals(ThingStatus.ONLINE)) {
244             // Bridge was online but the serial interface is now down
245             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, message);
246             logger.debug("Changed the Bridge status to offline because {}.", message);
247         }
248     }
249 }