]> git.basschouten.com Git - openhab-addons.git/blob
b95c5c0cf29267cdde2fcd1791793bf60a83a7f6
[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.resol.handler;
14
15 import java.beans.PropertyChangeEvent;
16 import java.beans.PropertyChangeListener;
17 import java.util.Objects;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.resol.internal.ResolEmuEMConfiguration;
24 import org.openhab.core.library.types.DecimalType;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.unit.SIUnits;
28 import org.openhab.core.library.unit.Units;
29 import org.openhab.core.thing.Bridge;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.thing.binding.ThingHandler;
35 import org.openhab.core.types.Command;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import de.resol.vbus.Connection;
40 import de.resol.vbus.Connection.ConnectionState;
41 import de.resol.vbus.Packet;
42 import de.resol.vbus.Specification;
43 import de.resol.vbus.SpecificationFile.Language;
44 import de.resol.vbus.deviceemulators.EmDeviceEmulator;
45
46 /**
47  * The {@link ResolEmuEMThingHandler} is responsible for emulating an EM device
48  *
49  * @author Raphael Mack - Initial contribution
50  */
51 @NonNullByDefault
52 public class ResolEmuEMThingHandler extends ResolBaseThingHandler implements PropertyChangeListener {
53     public static final String CHANNEL_RELAY = "relay_";
54     public static final String CHANNEL_TEMP = "temperature_";
55     public static final String CHANNEL_RESIST = "resistor_";
56     public static final String CHANNEL_SWITCH = "switch_";
57     public static final String CHANNEL_TEMP_ADJUST = "bas_temp_adjust_";
58     public static final String CHANNEL_MODE = "bas_mode_";
59
60     private final Logger logger = LoggerFactory.getLogger(ResolEmuEMThingHandler.class);
61
62     private int vbusAddress = 0x6650;
63     private int deviceId = 1;
64     private @Nullable EmDeviceEmulator device;
65
66     private @Nullable ResolBridgeHandler bridgeHandler;
67
68     private static class BasSetting {
69         float temperatureOffset = 0.0f;
70         int mode = 4;
71     }
72
73     private BasSetting[] basValues = { new BasSetting(), new BasSetting(), new BasSetting(), new BasSetting(),
74             new BasSetting(), new BasSetting() };
75     private long lastTime = System.currentTimeMillis();
76
77     // Background Runnable
78     private @Nullable ScheduledFuture<?> updateJob;
79
80     public ResolEmuEMThingHandler(Thing thing) {
81         super(thing);
82     }
83
84     @Override
85     public void initialize() {
86         ResolEmuEMConfiguration configuration = getConfigAs(ResolEmuEMConfiguration.class);
87         deviceId = configuration.deviceId;
88         vbusAddress = 0x6650 + deviceId;
89
90         bridgeHandler = getBridgeHandler();
91         registerResolThingListener(bridgeHandler);
92     }
93
94     @Override
95     public void dispose() {
96         EmDeviceEmulator dev = device;
97         ScheduledFuture<?> job = updateJob;
98         if (job != null) {
99             job.cancel(true);
100         }
101         if (dev != null) {
102             dev.stop();
103             dev.removePropertyChangeListener(this);
104         }
105     }
106
107     private void updateRunnable() {
108         EmDeviceEmulator d = device;
109         if (d != null) {
110             long now = System.currentTimeMillis();
111             int diff = (int) (now - lastTime);
112             lastTime = now;
113
114             d.update(diff);
115         }
116     }
117
118     private void startAutomaticUpdate() {
119         ScheduledFuture<?> job = updateJob;
120         if (job == null || job.isCancelled()) {
121             updateJob = scheduler.scheduleWithFixedDelay(this::updateRunnable, 0, 1, TimeUnit.SECONDS);
122         }
123     }
124
125     private synchronized @Nullable ResolBridgeHandler getBridgeHandler() {
126         Bridge bridge = getBridge();
127         if (bridge == null) {
128             logger.debug("Required bridge not defined for thing {}.", thing.getThingTypeUID());
129             return null;
130         } else {
131             return getBridgeHandler(bridge);
132         }
133     }
134
135     private synchronized @Nullable ResolBridgeHandler getBridgeHandler(Bridge bridge) {
136         ResolBridgeHandler bridgeHandler = null;
137
138         ThingHandler handler = bridge.getHandler();
139         if (handler instanceof ResolBridgeHandler) {
140             bridgeHandler = (ResolBridgeHandler) handler;
141         } else {
142             logger.debug("No available bridge handler found yet. Bridge: {} .", bridge.getUID());
143         }
144         return bridgeHandler;
145     }
146
147     private void registerResolThingListener(@Nullable ResolBridgeHandler bridgeHandler) {
148         if (bridgeHandler != null) {
149             bridgeHandler.registerResolThingListener(this);
150         } else {
151             logger.debug("Can't register {} at bridge as bridgeHandler is null.", this.getThing().getUID());
152         }
153     }
154
155     public int getVbusAddress() {
156         return vbusAddress;
157     }
158
159     public void useConnection(Connection connection) {
160         EmDeviceEmulator device = this.device;
161         if (device != null) {
162             device.stop();
163             device.removePropertyChangeListener(this);
164         }
165         device = new EmDeviceEmulator(connection, deviceId);
166         this.device = device;
167         device.addPropertyChangeListener(this);
168         device.start();
169         for (int i = 1; i <= 5; i++) {
170             setRelayChannelValue(i, device.getRelayValueByNr(i));
171         }
172         startAutomaticUpdate();
173     }
174
175     public void stop() {
176         EmDeviceEmulator device = this.device;
177         if (device != null) {
178             device.stop();
179         }
180         ScheduledFuture<?> updateJob = this.updateJob;
181         if (updateJob != null) {
182             updateJob.cancel(false);
183         }
184     }
185
186     @Override
187     public void handleCommand(ChannelUID channelUID, Command command) {
188         String chID = channelUID.getId();
189         int channel = chID.charAt(chID.length() - 1) - '0';
190         float value = 0;
191         int intValue = 0;
192
193         if (command instanceof QuantityType<?>) {
194             value = Objects.requireNonNullElse(((QuantityType<?>) command).toUnit(SIUnits.CELSIUS),
195                     new QuantityType<>(888.8, SIUnits.CELSIUS)).floatValue();
196         } else if (command instanceof OnOffType) {
197             intValue = ((OnOffType) command).equals(OnOffType.ON) ? 1 : 0;
198         } else if (command instanceof DecimalType) {
199             intValue = ((DecimalType) command).intValue();
200             value = intValue;
201         } else {
202             /* nothing to do */
203             return;
204         }
205
206         EmDeviceEmulator dev = device;
207         if (dev != null) {
208             if (chID.startsWith(CHANNEL_TEMP)) {
209                 dev.setResistorValueByNrAndPt1000Temperatur(channel, value);
210                 updateState(channelUID, new QuantityType<>(value, SIUnits.CELSIUS));
211             } else if (chID.startsWith(CHANNEL_SWITCH)) {
212                 if (intValue == 0) {
213                     /* switch is open => 1 megaohm */
214                     dev.setResistorValueByNr(channel, 1000000000);
215                     updateState(channelUID, OnOffType.OFF);
216                 } else {
217                     /* switch is closed */
218                     dev.setResistorValueByNr(channel, 0);
219                     updateState(channelUID, OnOffType.ON);
220                 }
221             } else if (chID.startsWith(CHANNEL_RESIST)) {
222                 dev.setResistorValueByNr(channel, (int) (value * 1000.0));
223                 updateState(channelUID, new QuantityType<>(intValue, Units.OHM));
224             } else if (chID.startsWith(CHANNEL_TEMP_ADJUST)) {
225                 basValues[channel - 1].temperatureOffset = value;
226                 updateBas(channel);
227                 updateState(channelUID, new QuantityType<>(value, SIUnits.CELSIUS));
228             } else if (chID.startsWith(CHANNEL_MODE)) {
229                 basValues[channel - 1].mode = intValue;
230                 updateBas(channel);
231                 updateState(channelUID, new QuantityType<>(intValue, Units.ONE));
232             } else {
233                 /* set resistor value for Open Connection, 1 megaohm */
234                 dev.setResistorValueByNr(channel, 1000000000);
235                 updateState(channelUID, new QuantityType<>(1000000, Units.OHM));
236             }
237         }
238     }
239
240     private void updateBas(int channel) {
241         int resistor = 0; /* in milliohm */
242         int delta = (int) ((basValues[channel - 1].temperatureOffset * 210.0f / 15.0f) * 1000.0f);
243         switch (basValues[channel - 1].mode) {
244             case 4: /* Automatic range 76 - 496 ohm */
245                 resistor = 286 * 1000 + delta;
246                 break;
247             case 0: /* OFF range 1840 - 2260 ohm */
248                 resistor = 2050 * 1000 + delta;
249                 break;
250             case 2: /* Night range 660 - 1080 ohm */
251                 resistor = 870 * 1000 + delta;
252                 break;
253             case 3: /* Party is automatic mode with +15K */
254                 resistor = 286 * 1000 + 210 * 1000;
255                 break;
256             case 1: /* Summer range 1240 - 1660 ohm */
257                 resistor = 1450 * 1000 + delta;
258                 break;
259             default:
260                 /* signal a shortcut as error */
261                 resistor = 0;
262                 break;
263         }
264         EmDeviceEmulator device = this.device;
265         if (device != null) {
266             device.setResistorValueByNr(channel, resistor);
267         }
268     }
269
270     @Override
271     public void propertyChange(@Nullable PropertyChangeEvent evt) {
272         if (evt != null) {
273             String s = evt.getPropertyName();
274             if (s.startsWith("relay") && s.endsWith("Value")) {
275                 int v = (Integer) evt.getNewValue();
276                 int i = Integer.parseInt(s.substring(5, 6));
277                 setRelayChannelValue(i, v);
278             } else if (s.contentEquals("connectionState")) {
279                 ConnectionState ste = (ConnectionState) evt.getNewValue();
280                 if (ste.equals(ConnectionState.CONNECTED)) {
281                     updateStatus(ThingStatus.ONLINE, ThingStatusDetail.NONE);
282                 } else {
283                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, ste.toString());
284                 }
285             }
286         }
287     }
288
289     private void setRelayChannelValue(int relay, double value) {
290         String channelId = CHANNEL_RELAY + relay;
291         updateState(channelId, new DecimalType(value));
292     }
293
294     @Override
295     public void packetReceived(Specification spec, Language lang, Packet packet) {
296         /* nothing to do here */
297     }
298 }