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.millheat.internal.handler;
15 import java.util.ArrayList;
16 import java.util.List;
17 import java.util.Optional;
19 import org.eclipse.jdt.annotation.NonNullByDefault;
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.openhab.binding.millheat.internal.MillheatBindingConstants;
22 import org.openhab.binding.millheat.internal.config.MillheatHeaterConfiguration;
23 import org.openhab.binding.millheat.internal.model.Heater;
24 import org.openhab.binding.millheat.internal.model.MillheatModel;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.OpenClosedType;
27 import org.openhab.core.library.types.QuantityType;
28 import org.openhab.core.library.unit.SIUnits;
29 import org.openhab.core.library.unit.Units;
30 import org.openhab.core.thing.Channel;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.thing.binding.builder.ChannelBuilder;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.RefreshType;
38 import org.openhab.core.types.UnDefType;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
43 * The {@link MillheatHeaterHandler} is responsible for handling commands, which are
44 * sent to one of the channels.
46 * @author Arne Seime - Initial contribution
49 public class MillheatHeaterHandler extends MillheatBaseThingHandler {
50 private final Logger logger = LoggerFactory.getLogger(MillheatHeaterHandler.class);
51 private @NonNullByDefault({}) MillheatHeaterConfiguration config;
53 public MillheatHeaterHandler(final Thing thing) {
58 public void handleCommand(final ChannelUID channelUID, final Command command) {
59 handleCommand(channelUID, command, getMillheatModel());
63 protected void handleCommand(final ChannelUID channelUID, final Command command, final MillheatModel model) {
64 final Optional<Heater> optionalHeater = model.findHeaterByMacOrId(config.macAddress, config.heaterId);
65 if (optionalHeater.isPresent()) {
66 updateStatus(ThingStatus.ONLINE);
67 final Heater heater = optionalHeater.get();
68 if (MillheatBindingConstants.CHANNEL_CURRENT_TEMPERATURE.equals(channelUID.getId())) {
69 if (command instanceof RefreshType) {
70 updateState(channelUID, new QuantityType<>(heater.getCurrentTemp(), SIUnits.CELSIUS));
72 } else if (MillheatBindingConstants.CHANNEL_HEATING_ACTIVE.equals(channelUID.getId())) {
73 if (command instanceof RefreshType) {
74 updateState(channelUID, heater.isHeatingActive() ? OnOffType.ON : OnOffType.OFF);
76 } else if (MillheatBindingConstants.CHANNEL_FAN_ACTIVE.equals(channelUID.getId())) {
77 if (command instanceof RefreshType) {
78 updateState(channelUID, heater.fanActive() ? OnOffType.ON : OnOffType.OFF);
79 } else if (heater.canChangeTemp() && heater.getRoom() == null) {
80 updateIndependentHeaterProperties(null, null, command);
82 logger.debug("Heater {} cannot change temperature and is in a room", getThing().getUID());
84 } else if (MillheatBindingConstants.CHANNEL_WINDOW_STATE.equals(channelUID.getId())) {
85 if (command instanceof RefreshType) {
86 updateState(channelUID, heater.windowOpen() ? OpenClosedType.OPEN : OpenClosedType.CLOSED);
88 } else if (MillheatBindingConstants.CHANNEL_INDEPENDENT.equals(channelUID.getId())) {
89 if (command instanceof RefreshType) {
90 updateState(channelUID, heater.getRoom() == null ? OnOffType.ON : OnOffType.OFF);
92 } else if (MillheatBindingConstants.CHANNEL_CURRENT_POWER.equals(channelUID.getId())) {
93 if (command instanceof RefreshType) {
94 if (config.power != null) {
95 if (heater.isHeatingActive()) {
96 updateState(channelUID, new QuantityType<>(config.power, Units.WATT));
98 updateState(channelUID, new QuantityType<>(0, Units.WATT));
101 updateState(channelUID, UnDefType.UNDEF);
103 "Cannot update power for heater as the nominal power has not been configured for thing {}",
104 getThing().getUID());
107 } else if (MillheatBindingConstants.CHANNEL_TARGET_TEMPERATURE.equals(channelUID.getId())) {
108 if (command instanceof RefreshType) {
109 if (heater.canChangeTemp() && heater.getTargetTemp() != null) {
110 updateState(channelUID, new QuantityType<>(heater.getTargetTemp(), SIUnits.CELSIUS));
111 } else if (heater.getRoom() != null) {
112 final Integer targetTemperature = heater.getRoom().getTargetTemperature();
113 if (targetTemperature != null) {
114 updateState(channelUID, new QuantityType<>(targetTemperature, SIUnits.CELSIUS));
116 updateState(channelUID, UnDefType.UNDEF);
120 "Heater {} is neither connected to a room nor marked as standalone. Someting is wrong, heater data: {}",
121 getThing().getUID(), heater);
122 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
125 if (heater.canChangeTemp() && heater.getRoom() == null) {
126 updateIndependentHeaterProperties(command, null, null);
129 } else if (MillheatBindingConstants.CHANNEL_MASTER_SWITCH.equals(channelUID.getId())) {
130 if (command instanceof RefreshType) {
131 updateState(channelUID, heater.powerStatus() ? OnOffType.ON : OnOffType.OFF);
133 if (heater.canChangeTemp() && heater.getRoom() == null) {
134 updateIndependentHeaterProperties(null, command, null);
136 // Just overwrite with old state
137 updateState(channelUID, heater.powerStatus() ? OnOffType.ON : OnOffType.OFF);
141 logger.debug("Received command {} on channel {}, but this channel is not handled or supported by {}",
142 channelUID.getId(), command.toString(), this.getThing().getUID());
145 updateStatus(ThingStatus.OFFLINE);
149 private void updateIndependentHeaterProperties(@Nullable final Command temperatureCommand,
150 @Nullable final Command masterOnOffCommand, @Nullable final Command fanCommand) {
151 getAccountHandler().ifPresent(handler -> {
152 handler.updateIndependentHeaterProperties(config.macAddress, config.heaterId, temperatureCommand,
153 masterOnOffCommand, fanCommand);
158 public void initialize() {
159 config = getConfigAs(MillheatHeaterConfiguration.class);
160 logger.debug("Initializing Millheat heater using config {}", config);
161 if (config.heaterId == null && config.macAddress == null) {
162 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
164 final Optional<Heater> heater = getMillheatModel().findHeaterByMacOrId(config.macAddress, config.heaterId);
165 if (heater.isPresent()) {
166 addOptionalChannels(heater.get());
167 updateStatus(ThingStatus.ONLINE);
169 updateStatus(ThingStatus.OFFLINE);
174 private void addOptionalChannels(final Heater heater) {
175 final List<Channel> newChannels = new ArrayList<>();
176 newChannels.addAll(getThing().getChannels());
177 if (heater.canChangeTemp() && heater.getRoom() == null) {
178 // Add power switch channel
181 .create(new ChannelUID(getThing().getUID(), MillheatBindingConstants.CHANNEL_MASTER_SWITCH),
183 .withType(MillheatBindingConstants.CHANNEL_TYPE_MASTER_SWITCH_UID).build());
184 // Add independent heater target temperature
185 newChannels.add(ChannelBuilder
186 .create(new ChannelUID(getThing().getUID(), MillheatBindingConstants.CHANNEL_TARGET_TEMPERATURE),
187 "Number:Temperature")
188 .withType(MillheatBindingConstants.CHANNEL_TYPE_TARGET_TEMPERATURE_HEATER_UID).build());
191 updateThing(editThing().withChannels(newChannels).build());