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.cm11a.internal.handler;
15 import java.util.List;
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;
34 import gnu.io.NoSuchPortException;
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.
42 * @author Bob Raker - Initial contribution
44 public class Cm11aBridgeHandler extends BaseBridgeHandler implements ReceivedDataListener {
46 private Cm11aConfig cm11aConfig;
48 private X10Interface x10Interface;
50 private Bridge bridge;
52 private final Logger logger = LoggerFactory.getLogger(Cm11aBridgeHandler.class);
54 public Cm11aBridgeHandler(Bridge bridge) {
59 public Cm11aConfig getCm11AConfig() {
60 return this.cm11aConfig;
64 public void handleCommand(ChannelUID channelUID, Command command) {
65 // Commands are handled by the "Things" which include Cm11aSwitchHandler and Cm11aLampHandler
69 public void initialize() {
70 // Get serial port number from config
71 cm11aConfig = getThing().getConfiguration().as(Cm11aConfig.class);
72 logger.trace("********* cm11a initialize started *********");
74 // Verify the configuration is valid
75 if (!validateConfig(this.cm11aConfig)) {
79 // Initialize the X10 interface
81 x10Interface = new X10Interface(cm11aConfig.serialPort, this);
82 x10Interface.setDaemon(true);
84 x10Interface.addReceivedDataListener(this);
85 logger.info("Initialized CM11A X10 interface on: {}", cm11aConfig.serialPort);
86 } catch (NoSuchPortException e) {
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);
94 updateStatus(ThingStatus.ONLINE);
98 public void dispose() {
99 // Close the serial port
100 if (x10Interface != null) {
101 x10Interface.disconnect();
104 logger.debug("Cm11aBridgeHandler is being removed.");
108 * Validate that the configuration is valid
110 * @param cm11aConfig2
113 private boolean validateConfig(Cm11aConfig config) {
114 if (config == null) {
115 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "cm11a configuration missing");
119 String port = config.serialPort;
120 if (port == null || port.isEmpty()) {
121 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "cm11a serialPort not specified");
131 * @see org.openhab.binding.cm11a.handler.ReceivedDataListener#receivedX10Data(org.openhab.binding.cm11a.internal.
135 public void receivedX10Data(X10ReceivedData rd) {
136 logger.debug("Cm11aReceivedDataManager received the following data: {}", rd);
138 List<Thing> things = bridge.getThings();
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.
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) {
155 X10COMMAND cmd = rd.getCmd();
156 int dims = rd.getDims();
157 updateX10State(thing, desiredChannelUid, cmd, dims);
165 * Find the X10 channel.
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();
179 return desiredChannelUid;
183 * Update the X10 state
190 private void updateX10State(Thing thing, ChannelUID channelUid, X10COMMAND cmd, int dims) {
192 logger.debug("Unable to update X10 state: thing is null");
196 Cm11aAbstractHandler handler = (Cm11aAbstractHandler) thing.getHandler();
197 if (handler == null) {
198 logger.debug("Unable to update X10 state: handler is null");
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());
222 logger.warn("Received unknown command from cm11a: {}", cmd);
227 * Get the X10Interface
229 * @return the X10Interface
231 public X10Interface getX10Interface() {
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.");
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);