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.avmfritz.internal.handler;
15 import static org.openhab.binding.avmfritz.internal.AVMFritzBindingConstants.*;
17 import java.math.BigDecimal;
18 import java.time.Instant;
19 import java.time.ZoneId;
20 import java.time.ZonedDateTime;
21 import java.util.List;
22 import java.util.Optional;
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.openhab.binding.avmfritz.internal.dto.AVMFritzBaseModel;
27 import org.openhab.binding.avmfritz.internal.dto.BatteryModel;
28 import org.openhab.binding.avmfritz.internal.dto.ButtonModel;
29 import org.openhab.binding.avmfritz.internal.dto.DeviceModel;
30 import org.openhab.binding.avmfritz.internal.dto.HumidityModel;
31 import org.openhab.binding.avmfritz.internal.dto.TemperatureModel;
32 import org.openhab.core.library.types.DateTimeType;
33 import org.openhab.core.library.types.DecimalType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.QuantityType;
36 import org.openhab.core.library.unit.SIUnits;
37 import org.openhab.core.library.unit.Units;
38 import org.openhab.core.thing.Channel;
39 import org.openhab.core.thing.ChannelUID;
40 import org.openhab.core.thing.CommonTriggerEvents;
41 import org.openhab.core.thing.Thing;
42 import org.openhab.core.thing.ThingUID;
43 import org.openhab.core.types.UnDefType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * Handler for a FRITZ! buttons. Handles commands, which are sent to one of the channels.
50 * @author Christoph Weitkamp - Initial contribution
53 public class AVMFritzButtonHandler extends DeviceHandler {
55 private static final String TOP_RIGHT_SUFFIX = "-1";
56 private static final String BOTTOM_RIGHT_SUFFIX = "-3";
57 private static final String BOTTOM_LEFT_SUFFIX = "-5";
58 private static final String TOP_LEFT_SUFFIX = "-7";
60 private final Logger logger = LoggerFactory.getLogger(AVMFritzButtonHandler.class);
62 * keeps track of the last timestamp for handling trigger events
64 private Instant lastTimestamp;
69 * @param thing Thing object representing a FRITZ! button
71 public AVMFritzButtonHandler(Thing thing) {
73 lastTimestamp = Instant.now();
77 public void onDeviceUpdated(ThingUID thingUID, AVMFritzBaseModel device) {
78 if (thing.getUID().equals(thingUID)) {
79 super.onDeviceUpdated(thingUID, device);
81 if (device instanceof DeviceModel) {
82 DeviceModel deviceModel = (DeviceModel) device;
83 if (deviceModel.isHANFUNButton()) {
84 updateHANFUNButton(deviceModel.getButtons());
86 if (deviceModel.isButton()) {
87 if (DECT400_THING_TYPE.equals(thing.getThingTypeUID())) {
88 updateShortLongPressButton(deviceModel.getButtons());
89 } else if (DECT440_THING_TYPE.equals(thing.getThingTypeUID())) {
90 updateButtons(deviceModel.getButtons());
92 updateBattery(deviceModel);
99 protected void updateTemperatureSensor(@Nullable TemperatureModel temperatureModel) {
100 if (temperatureModel != null) {
101 String channelId = (DECT440_THING_TYPE.equals(thing.getThingTypeUID())
102 ? CHANNEL_GROUP_SENSORS + ChannelUID.CHANNEL_GROUP_SEPARATOR
103 : "") + CHANNEL_TEMPERATURE;
104 updateThingChannelState(channelId, new QuantityType<>(temperatureModel.getCelsius(), SIUnits.CELSIUS));
105 updateThingChannelConfiguration(channelId, CONFIG_CHANNEL_TEMP_OFFSET, temperatureModel.getOffset());
110 protected void updateHumiditySensor(@Nullable HumidityModel humidityModel) {
111 if (humidityModel != null) {
112 String channelId = (DECT440_THING_TYPE.equals(thing.getThingTypeUID())
113 ? CHANNEL_GROUP_SENSORS + ChannelUID.CHANNEL_GROUP_SEPARATOR
114 : "") + CHANNEL_HUMIDITY;
115 updateThingChannelState(channelId, new QuantityType<>(humidityModel.getRelativeHumidity(), Units.PERCENT));
120 protected void updateBattery(BatteryModel batteryModel) {
121 String batteryLevelChannelId = (DECT440_THING_TYPE.equals(thing.getThingTypeUID())
122 ? CHANNEL_GROUP_DEVICE + ChannelUID.CHANNEL_GROUP_SEPARATOR
123 : "") + CHANNEL_BATTERY;
124 BigDecimal batteryLevel = batteryModel.getBattery();
125 updateThingChannelState(batteryLevelChannelId,
126 batteryLevel == null ? UnDefType.UNDEF : new DecimalType(batteryLevel));
127 String lowBatteryChannelId = (DECT440_THING_TYPE.equals(thing.getThingTypeUID())
128 ? CHANNEL_GROUP_DEVICE + ChannelUID.CHANNEL_GROUP_SEPARATOR
129 : "") + CHANNEL_BATTERY_LOW;
130 BigDecimal lowBattery = batteryModel.getBatterylow();
131 if (lowBattery == null) {
132 updateThingChannelState(lowBatteryChannelId, UnDefType.UNDEF);
134 updateThingChannelState(lowBatteryChannelId,
135 BatteryModel.BATTERY_ON.equals(lowBattery) ? OnOffType.ON : OnOffType.OFF);
139 private void updateShortLongPressButton(List<ButtonModel> buttons) {
140 ButtonModel shortPressButton = !buttons.isEmpty() ? buttons.get(0) : null;
141 ButtonModel longPressButton = buttons.size() > 1 ? buttons.get(1) : null;
142 ButtonModel lastPressedButton = shortPressButton != null && (longPressButton == null
143 || shortPressButton.getLastpressedtimestamp() > longPressButton.getLastpressedtimestamp())
146 if (lastPressedButton != null) {
147 updateButton(lastPressedButton,
148 lastPressedButton.equals(shortPressButton) ? CommonTriggerEvents.SHORT_PRESSED
149 : CommonTriggerEvents.LONG_PRESSED);
153 private void updateButtons(List<ButtonModel> buttons) {
154 Optional<ButtonModel> topLeft = buttons.stream().filter(b -> b.getIdentifier().endsWith(TOP_LEFT_SUFFIX))
156 if (topLeft.isPresent()) {
157 updateButton(topLeft.get(), CommonTriggerEvents.PRESSED, CHANNEL_GROUP_TOP_LEFT);
159 Optional<ButtonModel> bottomLeft = buttons.stream().filter(b -> b.getIdentifier().endsWith(BOTTOM_LEFT_SUFFIX))
161 if (bottomLeft.isPresent()) {
162 updateButton(bottomLeft.get(), CommonTriggerEvents.PRESSED, CHANNEL_GROUP_BOTTOM_LEFT);
164 Optional<ButtonModel> topRight = buttons.stream().filter(b -> b.getIdentifier().endsWith(TOP_RIGHT_SUFFIX))
166 if (topRight.isPresent()) {
167 updateButton(topRight.get(), CommonTriggerEvents.PRESSED, CHANNEL_GROUP_TOP_RIGHT);
169 Optional<ButtonModel> bottomRight = buttons.stream()
170 .filter(b -> b.getIdentifier().endsWith(BOTTOM_RIGHT_SUFFIX)).findFirst();
171 if (bottomRight.isPresent()) {
172 updateButton(bottomRight.get(), CommonTriggerEvents.PRESSED, CHANNEL_GROUP_BOTTOM_RIGHT);
176 private void updateHANFUNButton(List<ButtonModel> buttons) {
177 if (!buttons.isEmpty()) {
178 updateButton(buttons.get(0), CommonTriggerEvents.PRESSED);
182 private void updateButton(ButtonModel buttonModel, String event) {
183 updateButton(buttonModel, event, null);
186 private void updateButton(ButtonModel buttonModel, String event, @Nullable String channelGroupId) {
187 int lastPressedTimestamp = buttonModel.getLastpressedtimestamp();
188 if (lastPressedTimestamp == 0) {
189 updateThingChannelState(
190 channelGroupId == null ? CHANNEL_LAST_CHANGE
191 : channelGroupId + ChannelUID.CHANNEL_GROUP_SEPARATOR + CHANNEL_LAST_CHANGE,
194 ZonedDateTime timestamp = ZonedDateTime.ofInstant(Instant.ofEpochSecond(lastPressedTimestamp),
195 ZoneId.systemDefault());
196 Instant then = timestamp.toInstant();
197 // Avoid dispatching events if "lastpressedtimestamp" is older than now "lastTimestamp" (e.g. during
199 if (then.isAfter(lastTimestamp)) {
200 lastTimestamp = then;
201 triggerThingChannel(channelGroupId == null ? CHANNEL_PRESS
202 : channelGroupId + ChannelUID.CHANNEL_GROUP_SEPARATOR + CHANNEL_PRESS, event);
204 updateThingChannelState(
205 channelGroupId == null ? CHANNEL_LAST_CHANGE
206 : channelGroupId + ChannelUID.CHANNEL_GROUP_SEPARATOR + CHANNEL_LAST_CHANGE,
207 new DateTimeType(timestamp));
212 * Triggers thing channels.
214 * @param channelId ID of the channel to be triggered.
215 * @param event Event to emit
217 private void triggerThingChannel(String channelId, String event) {
218 Channel channel = thing.getChannel(channelId);
219 if (channel != null) {
220 triggerChannel(channel.getUID(), event);
222 logger.debug("Channel '{}' in thing '{}' does not exist.", channelId, thing.getUID());