]> git.basschouten.com Git - openhab-addons.git/blob
9840d8a72b2b63211d6c8ad3bcff6f62c0d2e62e
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2024 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.warmup.internal.handler;
14
15 import static org.openhab.binding.warmup.internal.WarmupBindingConstants.*;
16
17 import java.util.Collection;
18 import java.util.Map;
19 import java.util.Set;
20
21 import org.eclipse.jdt.annotation.NonNullByDefault;
22 import org.eclipse.jdt.annotation.Nullable;
23 import org.openhab.binding.warmup.internal.action.WarmupActions;
24 import org.openhab.binding.warmup.internal.api.MyWarmupApi;
25 import org.openhab.binding.warmup.internal.api.MyWarmupApiException;
26 import org.openhab.binding.warmup.internal.model.query.LocationDTO;
27 import org.openhab.binding.warmup.internal.model.query.QueryResponseDTO;
28 import org.openhab.binding.warmup.internal.model.query.RoomDTO;
29 import org.openhab.core.library.types.OnOffType;
30 import org.openhab.core.library.types.QuantityType;
31 import org.openhab.core.library.types.StringType;
32 import org.openhab.core.library.unit.Units;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.thing.binding.ThingHandlerService;
38 import org.openhab.core.types.Command;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * @author James Melville - Initial contribution
44  */
45 @NonNullByDefault
46 public class RoomHandler extends WarmupThingHandler implements WarmupRefreshListener {
47
48     private final Logger logger = LoggerFactory.getLogger(RoomHandler.class);
49     private @Nullable RoomConfigurationDTO config;
50
51     public RoomHandler(Thing thing) {
52         super(thing);
53     }
54
55     @Override
56     public void initialize() {
57         super.initialize();
58         config = getConfigAs(RoomConfigurationDTO.class);
59         if (config.getSerialNumber().length() == 0) {
60             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Serial Number not configured");
61         } else {
62             super.refreshFromServer();
63         }
64     }
65
66     @Override
67     public void handleCommand(ChannelUID channelUID, Command command) {
68         super.handleCommand(channelUID, command);
69         if (CHANNEL_TARGET_TEMPERATURE.equals(channelUID.getId())
70                 && command instanceof QuantityType<?> quantityCommand) {
71             setOverride(quantityCommand);
72         }
73         if (CHANNEL_FIXED_TEMPERATURE.equals(channelUID.getId())
74                 && command instanceof QuantityType<?> quantityCommand) {
75             setFixed(quantityCommand);
76         }
77         if (CHANNEL_FROST_PROTECTION_MODE.equals(channelUID.getId()) && command instanceof OnOffType onOffCommand) {
78             toggleFrostProtectionMode(onOffCommand);
79         }
80         if (CHANNEL_RUN_MODE.equals(channelUID.getId()) && command instanceof StringType stringCommand) {
81             setRoomMode(stringCommand);
82         }
83     }
84
85     /**
86      * Process device list and populate room properties, status and state
87      *
88      * @param domain Data model representing all devices
89      */
90     @Override
91     public void refresh(@Nullable QueryResponseDTO domain) {
92         if (domain == null) {
93             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, "No data from bridge");
94         } else if (config != null) {
95             final String serialNumber = config.getSerialNumber();
96             for (LocationDTO location : domain.data().user().locations()) {
97                 for (RoomDTO room : location.rooms()) {
98                     if (room.thermostat4ies() != null && !room.thermostat4ies().isEmpty()
99                             && room.thermostat4ies().get(0).deviceSN().equals(serialNumber)) {
100                         if (room.thermostat4ies().get(0).lastPoll() > 10) {
101                             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR,
102                                     "Thermostat has not polled for 10 minutes");
103                         } else {
104                             updateStatus(ThingStatus.ONLINE);
105
106                             updateProperty(PROPERTY_ROOM_ID, room.getId());
107                             updateProperty(PROPERTY_ROOM_NAME, room.roomName());
108                             updateProperty(PROPERTY_LOCATION_ID, location.getId());
109                             updateProperty(PROPERTY_LOCATION_NAME, location.name());
110
111                             updateState(CHANNEL_CURRENT_TEMPERATURE, parseTemperature(room.currentTemp()));
112                             updateState(CHANNEL_TARGET_TEMPERATURE, parseTemperature(room.targetTemp()));
113                             updateState(CHANNEL_FIXED_TEMPERATURE, parseTemperature(room.fixedTemp()));
114                             updateState(CHANNEL_ENERGY, parseEnergy(room.energy()));
115                             updateState(CHANNEL_AIR_TEMPERATURE,
116                                     parseTemperature(room.thermostat4ies().get(0).airTemp()));
117                             updateState(CHANNEL_FLOOR1_TEMPERATURE,
118                                     parseTemperature(room.thermostat4ies().get(0).floor1Temp()));
119                             updateState(CHANNEL_FLOOR2_TEMPERATURE,
120                                     parseTemperature(room.thermostat4ies().get(0).floor2Temp()));
121                             updateState(CHANNEL_OVERRIDE_DURATION, parseDuration(room.overrideDur()));
122                             updateState(CHANNEL_RUN_MODE, parseString(room.runMode()));
123                             updateState(CHANNEL_FROST_PROTECTION_MODE,
124                                     OnOffType.from(room.runMode().equals(FROST_PROTECTION_MODE)));
125                         }
126                         return;
127                     }
128                 }
129             }
130             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Room not found");
131         } else {
132             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Room not configured");
133         }
134     }
135
136     @Override
137     public Collection<Class<? extends ThingHandlerService>> getServices() {
138         return Set.of(WarmupActions.class);
139     }
140
141     private void setOverride(final QuantityType<?> command) {
142         setOverride(command, new QuantityType<>(config.getOverrideDuration(), Units.MINUTE));
143     }
144
145     public void setOverride(final QuantityType<?> temperature, final QuantityType<?> duration) {
146         setOverride(formatTemperature(temperature), duration.toUnit(Units.MINUTE).intValue());
147     }
148
149     private void setOverride(final int temperature, final int duration) {
150         if (duration > 1440 || duration <= 0) {
151             logger.warn("Set Override failed: duration must be between 0 and 1440 minutes");
152         }
153         if (temperature > 600 || temperature < 50) {
154             logger.warn("Set Override failed: temperature must be between 0.5 and 60 degrees C");
155         } else {
156             try {
157                 RoomCallout rc = getCallout();
158                 rc.api.setOverride(rc.locationId, rc.roomId, temperature, duration);
159                 refreshFromServer();
160             } catch (MyWarmupApiException e) {
161                 logger.warn("Set Override failed: {}", e.getMessage());
162             }
163         }
164     }
165
166     private void setFixed(final QuantityType<?> command) {
167         try {
168             RoomCallout rc = getCallout();
169             rc.api.setFixed(rc.locationId, rc.roomId, formatTemperature(command));
170             refreshFromServer();
171         } catch (MyWarmupApiException e) {
172             logger.warn("Set Fixed failed: {}", e.getMessage());
173         }
174     }
175
176     private void toggleFrostProtectionMode(OnOffType command) {
177         try {
178             RoomCallout rc = getCallout();
179             rc.api.toggleFrostProtectionMode(rc.locationId, rc.roomId, command);
180             refreshFromServer();
181         } catch (MyWarmupApiException e) {
182             logger.warn("Toggle Frost Protection failed: {}", e.getMessage());
183         }
184     }
185
186     private void setRoomMode(StringType command) {
187         try {
188             RoomCallout rc = getCallout();
189             RoomMode mode = RoomMode.valueOf(command.toString().trim().toUpperCase());
190             rc.api.setRoomMode(rc.locationId, rc.roomId, mode);
191             refreshFromServer();
192         } catch (MyWarmupApiException e) {
193             logger.warn("Set Room Mode failed: {}", e.getMessage());
194         } catch (IllegalArgumentException ex) {
195             logger.warn("Unable to set room mode: {}", command.toString());
196         }
197     }
198
199     private RoomCallout getCallout() throws MyWarmupApiException {
200         Map<String, String> props = getThing().getProperties();
201         String locationId = props.get(PROPERTY_LOCATION_ID);
202         String roomId = props.get(PROPERTY_ROOM_ID);
203         final MyWarmupAccountHandler bridgeHandler = getBridgeHandler();
204
205         if (bridgeHandler != null && locationId != null && roomId != null) {
206             return new RoomCallout(roomId, locationId, bridgeHandler.getApi());
207         } else {
208             throw new MyWarmupApiException("Misconfigured thing.");
209         }
210     }
211
212     record RoomCallout(String roomId, String locationId, MyWarmupApi api) {
213     }
214 }