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.sinope.handler;
15 import java.io.IOException;
16 import java.net.UnknownHostException;
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.binding.sinope.SinopeBindingConstants;
21 import org.openhab.binding.sinope.internal.config.SinopeConfig;
22 import org.openhab.binding.sinope.internal.core.SinopeDataReadRequest;
23 import org.openhab.binding.sinope.internal.core.SinopeDataWriteRequest;
24 import org.openhab.binding.sinope.internal.core.appdata.SinopeHeatLevelData;
25 import org.openhab.binding.sinope.internal.core.appdata.SinopeOutTempData;
26 import org.openhab.binding.sinope.internal.core.appdata.SinopeRoomTempData;
27 import org.openhab.binding.sinope.internal.core.appdata.SinopeSetPointModeData;
28 import org.openhab.binding.sinope.internal.core.appdata.SinopeSetPointTempData;
29 import org.openhab.binding.sinope.internal.core.base.SinopeDataAnswer;
30 import org.openhab.binding.sinope.internal.util.ByteUtil;
31 import org.openhab.core.config.core.Configuration;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.QuantityType;
34 import org.openhab.core.library.unit.SIUnits;
35 import org.openhab.core.thing.Bridge;
36 import org.openhab.core.thing.ChannelUID;
37 import org.openhab.core.thing.Thing;
38 import org.openhab.core.thing.ThingStatus;
39 import org.openhab.core.thing.ThingStatusDetail;
40 import org.openhab.core.thing.ThingStatusInfo;
41 import org.openhab.core.thing.binding.BaseThingHandler;
42 import org.openhab.core.types.Command;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * The {@link SinopeThermostatHandler} is responsible for handling commands, which are
48 * sent to one of the channels.
50 * @author Pascal Larin - Initial contribution
53 public class SinopeThermostatHandler extends BaseThingHandler {
55 private static final int DATA_ANSWER = 0x0A;
57 private Logger logger = LoggerFactory.getLogger(SinopeThermostatHandler.class);
59 private byte[] deviceId = new byte[0];
61 public SinopeThermostatHandler(Thing thing) {
66 public void handleCommand(ChannelUID channelUID, Command command) {
67 if (getSinopeGatewayHandler() != null) {
69 if (SinopeBindingConstants.CHANNEL_SETTEMP.equals(channelUID.getId())
70 && command instanceof QuantityType quantityCommand) {
71 setSetpointTemp(quantityCommand.floatValue());
73 if (SinopeBindingConstants.CHANNEL_SETMODE.equals(channelUID.getId())
74 && command instanceof DecimalType decimalCommand) {
75 setSetpointMode(decimalCommand.intValue());
77 } catch (IOException e) {
78 logger.debug("Cannot handle command for channel {} because of {}", channelUID.getId(),
79 e.getLocalizedMessage());
80 getSinopeGatewayHandler().setCommunicationError(true);
85 private void setSetpointTemp(float temp) throws UnknownHostException, IOException {
86 int newTemp = (int) (temp * 100.0);
88 getSinopeGatewayHandler().stopPoll();
90 if (getSinopeGatewayHandler().connectToBridge()) {
91 logger.debug("Connected to bridge");
93 SinopeDataWriteRequest req = new SinopeDataWriteRequest(getSinopeGatewayHandler().newSeq(), deviceId,
94 new SinopeSetPointTempData());
95 ((SinopeSetPointTempData) req.getAppData()).setSetPointTemp(newTemp);
97 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
99 if (answ.getStatus() == DATA_ANSWER) {
100 logger.debug("Setpoint temp is now: {} C", temp);
102 logger.debug("Cannot Setpoint temp, status: {}", answ.getStatus());
105 logger.debug("Could not connect to bridge to update Setpoint Temp");
106 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect to bridge");
109 getSinopeGatewayHandler().schedulePoll();
113 private void setSetpointMode(int mode) throws UnknownHostException, IOException {
114 getSinopeGatewayHandler().stopPoll();
116 if (getSinopeGatewayHandler().connectToBridge()) {
117 logger.debug("Connected to bridge");
119 SinopeDataWriteRequest req = new SinopeDataWriteRequest(getSinopeGatewayHandler().newSeq(), deviceId,
120 new SinopeSetPointModeData());
121 ((SinopeSetPointModeData) req.getAppData()).setSetPointMode((byte) mode);
123 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
125 if (answ.getStatus() == DATA_ANSWER) {
126 logger.debug("Setpoint mode is now : {}", mode);
128 logger.debug("Cannot Setpoint mode, status: {}", answ.getStatus());
131 logger.debug("Could not connect to bridge to update Setpoint Temp");
132 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "Cannot connect to bridge");
135 getSinopeGatewayHandler().schedulePoll();
140 public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
141 logger.debug("bridgeStatusChanged {}", bridgeStatusInfo);
146 public void initialize() {
147 logger.debug("initializeThing thing {}", getThing().getUID());
152 protected void updateConfiguration(Configuration configuration) {
153 super.updateConfiguration(configuration);
157 public void updateOutsideTemp(double temp) {
158 updateState(SinopeBindingConstants.CHANNEL_OUTTEMP, new QuantityType<>(temp, SIUnits.CELSIUS));
161 public void updateRoomTemp(double temp) {
162 updateState(SinopeBindingConstants.CHANNEL_INTEMP, new QuantityType<>(temp, SIUnits.CELSIUS));
165 public void updateSetPointTemp(double temp) {
166 updateState(SinopeBindingConstants.CHANNEL_SETTEMP, new QuantityType<>(temp, SIUnits.CELSIUS));
169 public void updateSetPointMode(int mode) {
170 updateState(SinopeBindingConstants.CHANNEL_SETMODE, new DecimalType(mode));
173 public void updateHeatingLevel(int heatingLevel) {
174 updateState(SinopeBindingConstants.CHANNEL_HEATINGLEVEL, new DecimalType(heatingLevel));
177 public void update() throws UnknownHostException, IOException {
178 if (this.deviceId.length > 0 && getSinopeGatewayHandler() != null) {
179 if (isLinked(SinopeBindingConstants.CHANNEL_OUTTEMP)) {
180 this.updateOutsideTemp(readOutsideTemp());
182 if (isLinked(SinopeBindingConstants.CHANNEL_INTEMP)) {
183 this.updateRoomTemp(readRoomTemp());
185 if (isLinked(SinopeBindingConstants.CHANNEL_SETTEMP)) {
186 this.updateSetPointTemp(readSetpointTemp());
188 if (isLinked(SinopeBindingConstants.CHANNEL_SETMODE)) {
189 this.updateSetPointMode(readSetpointMode());
191 if (isLinked(SinopeBindingConstants.CHANNEL_HEATINGLEVEL)) {
192 this.updateHeatingLevel(readHeatLevel());
195 logger.error("Device id is null for Thing UID: {}", getThing().getUID());
196 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
200 private double readRoomTemp() throws UnknownHostException, IOException {
201 logger.debug("Reading room temp for device id : {}", ByteUtil.toString(deviceId));
202 SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
203 new SinopeRoomTempData());
204 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
205 double temp = ((SinopeRoomTempData) answ.getAppData()).getRoomTemp() / 100.0;
206 logger.debug("Room temp is : {} C", temp);
210 private double readOutsideTemp() throws UnknownHostException, IOException {
211 SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
212 new SinopeOutTempData());
213 logger.debug("Reading outside temp for device id: {}", ByteUtil.toString(deviceId));
214 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
215 double temp = ((SinopeOutTempData) answ.getAppData()).getOutTemp() / 100.0;
216 logger.debug("Outside temp is : {} C", temp);
220 private double readSetpointTemp() throws UnknownHostException, IOException {
221 SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
222 new SinopeSetPointTempData());
223 logger.debug("Reading Set Point temp for device id: {}", ByteUtil.toString(deviceId));
224 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
225 double temp = ((SinopeSetPointTempData) answ.getAppData()).getSetPointTemp() / 100.0;
226 logger.debug("Setpoint temp is : {} C", temp);
230 private int readSetpointMode() throws UnknownHostException, IOException {
231 SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
232 new SinopeSetPointModeData());
233 logger.debug("Reading Set Point mode for device id: {}", ByteUtil.toString(deviceId));
234 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
235 int mode = ((SinopeSetPointModeData) answ.getAppData()).getSetPointMode();
236 logger.debug("Setpoint mode is : {}", mode);
240 private int readHeatLevel() throws UnknownHostException, IOException {
241 SinopeDataReadRequest req = new SinopeDataReadRequest(getSinopeGatewayHandler().newSeq(), deviceId,
242 new SinopeHeatLevelData());
243 logger.debug("Reading Heat Level for device id: {}", ByteUtil.toString(deviceId));
244 SinopeDataAnswer answ = (SinopeDataAnswer) getSinopeGatewayHandler().execute(req);
245 int level = ((SinopeHeatLevelData) answ.getAppData()).getHeatLevel();
246 logger.debug("Heating level is : {}", level);
250 private synchronized void updateDeviceId() {
251 String sDeviceId = (String) getConfig().get(SinopeBindingConstants.CONFIG_PROPERTY_DEVICE_ID);
252 this.deviceId = SinopeConfig.convert(sDeviceId);
253 if (this.deviceId.length == 0) {
254 logger.debug("Invalid Device id, cannot convert id: {}", sDeviceId);
255 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Invalid Device id");
259 Bridge bridge = getBridge();
260 if (bridge == null) {
261 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
264 SinopeGatewayHandler handler = getSinopeGatewayHandler();
265 if (handler != null) {
266 handler.registerThermostatHandler(this);
268 updateStatus(ThingStatus.ONLINE);
271 private @Nullable SinopeGatewayHandler getSinopeGatewayHandler() {
272 Bridge bridge = this.getBridge();
273 if (bridge != null) {
274 return (SinopeGatewayHandler) bridge.getHandler();