]> git.basschouten.com Git - openhab-addons.git/blob
8b7a03e4d9cda331845d8676720cd983a90a2115
[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.openwebnet.internal.handler;
14
15 import static org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants.CHANNEL_POWER;
16
17 import java.util.Set;
18 import java.util.concurrent.ScheduledFuture;
19 import java.util.concurrent.TimeUnit;
20
21 import javax.measure.quantity.Power;
22
23 import org.eclipse.jdt.annotation.NonNullByDefault;
24 import org.eclipse.jdt.annotation.Nullable;
25 import org.openhab.binding.openwebnet.internal.OpenWebNetBindingConstants;
26 import org.openhab.core.library.types.QuantityType;
27 import org.openhab.core.library.unit.Units;
28 import org.openhab.core.thing.ChannelUID;
29 import org.openhab.core.thing.Thing;
30 import org.openhab.core.thing.ThingStatus;
31 import org.openhab.core.thing.ThingStatusDetail;
32 import org.openhab.core.thing.ThingStatusInfo;
33 import org.openhab.core.thing.ThingTypeUID;
34 import org.openhab.core.types.Command;
35 import org.openhab.core.types.UnDefType;
36 import org.openwebnet4j.OpenGateway;
37 import org.openwebnet4j.communication.OWNException;
38 import org.openwebnet4j.message.BaseOpenMessage;
39 import org.openwebnet4j.message.EnergyManagement;
40 import org.openwebnet4j.message.FrameException;
41 import org.openwebnet4j.message.Where;
42 import org.openwebnet4j.message.WhereEnergyManagement;
43 import org.openwebnet4j.message.Who;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * The {@link OpenWebNetEnergyHandler} is responsible for handling commands/messages for an Energy Management OpenWebNet
49  * device. It extends the abstract {@link OpenWebNetThingHandler}.
50  *
51  * @author Massimo Valla - Initial contribution
52  * @author Andrea Conte - Energy management
53  */
54 @NonNullByDefault
55 public class OpenWebNetEnergyHandler extends OpenWebNetThingHandler {
56
57     private final Logger logger = LoggerFactory.getLogger(OpenWebNetEnergyHandler.class);
58
59     public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES = OpenWebNetBindingConstants.ENERGY_MANAGEMENT_SUPPORTED_THING_TYPES;
60     public static final int ENERGY_SUBSCRIPTION_PERIOD = 10; // minutes
61     private @Nullable ScheduledFuture<?> notificationSchedule;
62
63     public OpenWebNetEnergyHandler(Thing thing) {
64         super(thing);
65     }
66
67     public Boolean isFirstSchedulerLaunch = true;
68
69     @Override
70     public void initialize() {
71         super.initialize();
72
73         // In order to get data from the probe we must send a command over the bus, this could be done only when the
74         // bridge is online.
75         // a) usual flow: binding is starting, the bridge isn't online (startup flow not yet completed) --> subscriber
76         // can't be started here, it will be started inside the bridgeStatusChanged event.
77         // b) thing's discovery: binding is up and running, the bridge is online --> subscriber must be started here
78         // otherwise data will be read only after a reboot.
79
80         OpenWebNetBridgeHandler h = bridgeHandler;
81         if (h != null && h.isBusGateway()) {
82             OpenGateway gw = h.gateway;
83             if (gw != null && gw.isConnected()) {
84                 // bridge is online
85                 subscribeToActivePowerChanges();
86             }
87         }
88     }
89
90     @Override
91     public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
92         super.bridgeStatusChanged(bridgeStatusInfo);
93
94         // subscribe the scheduler only after the bridge is online
95         if (bridgeStatusInfo.getStatus().equals(ThingStatus.ONLINE)) {
96             subscribeToActivePowerChanges();
97         }
98     }
99
100     private void subscribeToActivePowerChanges() {
101         notificationSchedule = scheduler.scheduleWithFixedDelay(() -> {
102             if (isFirstSchedulerLaunch) {
103                 logger.debug(
104                         "subscribeToActivePowerChanges() For WHERE={} subscribing to active power changes notification for the next {}min",
105                         deviceWhere, ENERGY_SUBSCRIPTION_PERIOD);
106             } else {
107                 logger.debug(
108                         "subscribeToActivePowerChanges() Refreshing subscription for the next {}min for WHERE={} to active power changes notification",
109                         ENERGY_SUBSCRIPTION_PERIOD, deviceWhere);
110             }
111             Where w = deviceWhere;
112             if (w == null) {
113                 logger.warn("subscribeToActivePowerChanges() WHERE=null. Skipping");
114             } else {
115                 try {
116                     send(EnergyManagement.setActivePowerNotificationsTime(w.value(), ENERGY_SUBSCRIPTION_PERIOD));
117                     isFirstSchedulerLaunch = false;
118                 } catch (Exception e) {
119                     if (isFirstSchedulerLaunch) {
120                         logger.warn(
121                                 "subscribeToActivePowerChanges() For WHERE={} could not subscribe to active power changes notifications. Exception={}",
122                                 w, e.getMessage());
123                     } else {
124                         logger.warn(
125                                 "subscribeToActivePowerChanges() Unable to refresh subscription to active power changes notifications for WHERE={}. Exception={}",
126                                 w, e.getMessage());
127                     }
128                 }
129             }
130         }, 0, ENERGY_SUBSCRIPTION_PERIOD - 1, TimeUnit.MINUTES);
131     }
132
133     @Override
134     public void dispose() {
135         if (notificationSchedule != null) {
136             ScheduledFuture<?> ns = notificationSchedule;
137             ns.cancel(false);
138             logger.debug("dispose() scheduler stopped.");
139         }
140         super.dispose();
141     }
142
143     @Override
144     protected Where buildBusWhere(String wStr) throws IllegalArgumentException {
145         return new WhereEnergyManagement(wStr);
146     }
147
148     @Override
149     protected void requestChannelState(ChannelUID channel) {
150         super.requestChannelState(channel);
151         Where w = deviceWhere;
152         if (w != null) {
153             try {
154                 send(EnergyManagement.requestActivePower(w.value()));
155             } catch (OWNException e) {
156                 logger.debug("Exception while requesting state for channel {}: {} ", channel, e.getMessage());
157                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
158             }
159         }
160     }
161
162     @Override
163     protected void refreshDevice(boolean refreshAll) {
164         logger.debug("--- refreshDevice() : refreshing SINGLE... ({})", thing.getUID());
165         requestChannelState(new ChannelUID(thing.getUID(), CHANNEL_POWER));
166     }
167
168     @Override
169     protected void handleChannelCommand(ChannelUID channel, Command command) {
170         logger.warn("handleChannelCommand() Read only channel, unsupported command {}", command);
171     }
172
173     @Override
174     protected String ownIdPrefix() {
175         return Who.ENERGY_MANAGEMENT.value().toString();
176     }
177
178     @Override
179     protected void handleMessage(BaseOpenMessage msg) {
180         super.handleMessage(msg);
181
182         if (msg.isCommand()) {
183             logger.warn("handleMessage() Ignoring unsupported command for thing {}. Frame={}", getThing().getUID(),
184                     msg);
185             return;
186         } else {
187             // fix: check for correct DIM (ActivePower / 113)
188             if (msg.getDim().equals(EnergyManagement.DimEnergyMgmt.ACTIVE_POWER)) {
189                 updateActivePower(msg);
190             } else {
191                 logger.debug("handleMessage() Ignoring message {} because it's not related to active power value.",
192                         msg);
193             }
194         }
195     }
196
197     /**
198      * Updates energy power state based on an EnergyManagement message received from the OWN network
199      *
200      * @param msg the EnergyManagement message received
201      * @throws FrameException
202      */
203     private void updateActivePower(BaseOpenMessage msg) {
204         Integer activePower;
205         try {
206             activePower = Integer.parseInt(msg.getDimValues()[0]);
207             updateState(CHANNEL_POWER, new QuantityType<Power>(activePower, Units.WATT));
208         } catch (FrameException e) {
209             logger.warn("FrameException on frame {}: {}", msg, e.getMessage());
210             updateState(CHANNEL_POWER, UnDefType.UNDEF);
211         }
212     }
213 }