2 * Copyright (c) 2010-2024 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.warmup.internal.handler;
15 import static org.openhab.binding.warmup.internal.WarmupBindingConstants.*;
17 import java.util.Collection;
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;
43 * @author James Melville - Initial contribution
46 public class RoomHandler extends WarmupThingHandler implements WarmupRefreshListener {
48 private final Logger logger = LoggerFactory.getLogger(RoomHandler.class);
49 private @Nullable RoomConfigurationDTO config;
51 public RoomHandler(Thing thing) {
56 public void initialize() {
58 config = getConfigAs(RoomConfigurationDTO.class);
59 if (config.getSerialNumber().length() == 0) {
60 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Serial Number not configured");
62 super.refreshFromServer();
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);
73 if (CHANNEL_FIXED_TEMPERATURE.equals(channelUID.getId())
74 && command instanceof QuantityType<?> quantityCommand) {
75 setFixed(quantityCommand);
77 if (CHANNEL_FROST_PROTECTION_MODE.equals(channelUID.getId()) && command instanceof OnOffType onOffCommand) {
78 toggleFrostProtectionMode(onOffCommand);
80 if (CHANNEL_RUN_MODE.equals(channelUID.getId()) && command instanceof StringType stringCommand) {
81 setRoomMode(stringCommand);
86 * Process device list and populate room properties, status and state
88 * @param domain Data model representing all devices
91 public void refresh(@Nullable QueryResponseDTO domain) {
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");
104 updateStatus(ThingStatus.ONLINE);
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());
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)));
130 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Room not found");
132 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Room not configured");
137 public Collection<Class<? extends ThingHandlerService>> getServices() {
138 return Set.of(WarmupActions.class);
141 private void setOverride(final QuantityType<?> command) {
142 setOverride(command, new QuantityType<>(config.getOverrideDuration(), Units.MINUTE));
145 public void setOverride(final QuantityType<?> temperature, final QuantityType<?> duration) {
146 setOverride(formatTemperature(temperature), duration.toUnit(Units.MINUTE).intValue());
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");
153 if (temperature > 600 || temperature < 50) {
154 logger.warn("Set Override failed: temperature must be between 0.5 and 60 degrees C");
157 RoomCallout rc = getCallout();
158 rc.api.setOverride(rc.locationId, rc.roomId, temperature, duration);
160 } catch (MyWarmupApiException e) {
161 logger.warn("Set Override failed: {}", e.getMessage());
166 private void setFixed(final QuantityType<?> command) {
168 RoomCallout rc = getCallout();
169 rc.api.setFixed(rc.locationId, rc.roomId, formatTemperature(command));
171 } catch (MyWarmupApiException e) {
172 logger.warn("Set Fixed failed: {}", e.getMessage());
176 private void toggleFrostProtectionMode(OnOffType command) {
178 RoomCallout rc = getCallout();
179 rc.api.toggleFrostProtectionMode(rc.locationId, rc.roomId, command);
181 } catch (MyWarmupApiException e) {
182 logger.warn("Toggle Frost Protection failed: {}", e.getMessage());
186 private void setRoomMode(StringType command) {
188 RoomCallout rc = getCallout();
189 RoomMode mode = RoomMode.valueOf(command.toString().trim().toUpperCase());
190 rc.api.setRoomMode(rc.locationId, rc.roomId, mode);
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());
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();
205 if (bridgeHandler != null && locationId != null && roomId != null) {
206 return new RoomCallout(roomId, locationId, bridgeHandler.getApi());
208 throw new MyWarmupApiException("Misconfigured thing.");
212 record RoomCallout(String roomId, String locationId, MyWarmupApi api) {