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