]> git.basschouten.com Git - openhab-addons.git/blob
91d85248dd2205b359f2c632754e2e14f29a13ed
[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.solarwatt.internal.handler;
14
15 import static org.openhab.binding.solarwatt.internal.SolarwattBindingConstants.*;
16
17 import java.text.MessageFormat;
18 import java.util.Map;
19
20 import javax.measure.Unit;
21
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.solarwatt.internal.channel.SolarwattChannelTypeProvider;
25 import org.openhab.binding.solarwatt.internal.configuration.SolarwattThingConfiguration;
26 import org.openhab.binding.solarwatt.internal.domain.SolarwattChannel;
27 import org.openhab.binding.solarwatt.internal.domain.model.Device;
28 import org.openhab.core.library.CoreItemFactory;
29 import org.openhab.core.library.unit.Units;
30 import org.openhab.core.thing.Bridge;
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.BaseThingHandler;
36 import org.openhab.core.thing.binding.BridgeHandler;
37 import org.openhab.core.thing.binding.builder.ChannelBuilder;
38 import org.openhab.core.thing.binding.builder.ThingBuilder;
39 import org.openhab.core.thing.type.ChannelKind;
40 import org.openhab.core.thing.type.ChannelTypeUID;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.RefreshType;
43 import org.openhab.core.types.util.UnitUtils;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * The {@link SimpleDeviceHandler} bundles everything related to generic talking to devices.
49  *
50  * @author Sven Carstens - Initial contribution
51  */
52 @NonNullByDefault
53 public class SimpleDeviceHandler extends BaseThingHandler {
54     private final Logger logger = LoggerFactory.getLogger(SimpleDeviceHandler.class);
55
56     private final SolarwattChannelTypeProvider channelTypeProvider;
57
58     public SimpleDeviceHandler(Thing thing, SolarwattChannelTypeProvider channelTypeProvider) {
59         super(thing);
60         this.channelTypeProvider = channelTypeProvider;
61     }
62
63     /**
64      * Bring the thing online and update state from the bridge.
65      */
66     @Override
67     public void initialize() {
68         final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
69         if (bridgeHandler != null) {
70             this.initDeviceChannels();
71             this.updateDeviceProperties();
72             this.updateDeviceChannels();
73         } else {
74             this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
75                     "Received null bridge while initializing!");
76         }
77     }
78
79     /**
80      * Process the command for this thing.
81      *
82      * Only refresh is supported in this case.
83      *
84      * @param channelUID channel for which the command was issued
85      * @param command to execute
86      */
87     @Override
88     public void handleCommand(ChannelUID channelUID, Command command) {
89         final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
90         if (bridgeHandler != null) {
91             if (command instanceof RefreshType) {
92                 this.updateDeviceProperties();
93                 this.updateDeviceChannels();
94             }
95         } else {
96             this.logger.warn("Thing {} has no bridgeHandler for Bridge {}", this.getThing().getUID(),
97                     this.getThing().getBridgeUID());
98         }
99     }
100
101     /**
102      * Update the state of all channels.
103      */
104     protected void updateDeviceChannels() {
105         // find device for the thing
106         Device device = this.getDevice();
107
108         if (device != null) {
109             device.getStateValues().forEach(this::updateState);
110         }
111     }
112
113     /**
114      * Assert that all {@link org.openhab.core.thing.type.ChannelType}s are registered for this thing.
115      */
116     protected void initDeviceChannels() {
117         // find device for the thing
118         Device device = this.getDevice();
119
120         if (device != null) {
121             device.getSolarwattChannelSet().forEach((channelTag, solarwattChannel) -> {
122                 this.assertChannel(solarwattChannel);
123             });
124         }
125     }
126
127     /**
128      * Update the properties for this device.
129      */
130     protected void updateDeviceProperties() {
131         // find device for the thing
132         Device device = this.getDevice();
133
134         if (device != null) {
135             // update properties
136             Map<String, String> properties = this.editProperties();
137             this.putProperty(properties, PROPERTY_ID_NAME, device.getIdName());
138             this.putProperty(properties, PROPERTY_ID_FIRMWARE, device.getIdFirmware());
139             this.putProperty(properties, PROPERTY_ID_MANUFACTURER, device.getIdManufacturer());
140             this.updateProperties(properties);
141
142             // relay state of device to status
143             this.updateStatus(device.getStateDevice());
144         }
145     }
146
147     private void putProperty(Map<String, String> properties, String name, @Nullable String value) {
148         if (value != null) {
149             properties.put(name, value);
150         }
151     }
152
153     /**
154      * Assert that all channels inside of our thing are well defined.
155      *
156      * Only channels which can not be found are created.
157      *
158      * @param solarwattChannel channel description with name and unit
159      */
160     protected void assertChannel(SolarwattChannel solarwattChannel) {
161         ChannelUID channelUID = new ChannelUID(this.getThing().getUID(), solarwattChannel.getChannelName());
162         ChannelTypeUID channelType = this.channelTypeProvider.assertChannelType(solarwattChannel);
163         if (this.getThing().getChannel(channelUID) == null) {
164             ThingBuilder thingBuilder = this.editThing();
165             thingBuilder.withChannel(getChannelBuilder(solarwattChannel, channelUID, channelType).build());
166
167             this.updateThing(thingBuilder.build());
168         }
169     }
170
171     /**
172      * Get a builder for a channel type according to the {@link SolarwattChannel}
173      *
174      * @param solarwattChannel channel type definition
175      * @param channelUID uid of the channel
176      * @param channelType uid of the channel type
177      * @return builder for that channel type
178      */
179     public static ChannelBuilder getChannelBuilder(SolarwattChannel solarwattChannel, ChannelUID channelUID,
180             ChannelTypeUID channelType) {
181         String itemType = CoreItemFactory.STRING;
182         Unit<?> unit = solarwattChannel.getUnit();
183         if (unit != null) {
184             String dimension = UnitUtils.getDimensionName(unit);
185
186             if (Units.PERCENT.equals(unit)) {
187                 // strangely it is Angle
188                 dimension = ":Dimensionless";
189             }
190
191             itemType = CoreItemFactory.NUMBER;
192             if (dimension != null && !dimension.isEmpty()) {
193                 itemType = CoreItemFactory.NUMBER + ":" + dimension;
194             }
195         }
196         ChannelBuilder channelBuilder = ChannelBuilder.create(channelUID, itemType);
197
198         channelBuilder.withLabel(solarwattChannel.getChannelName()).withType(channelType).withDescription(MessageFormat
199                 .format("Value for {0} with Unit: {1}", solarwattChannel.getChannelName(), solarwattChannel.getUnit()))
200                 .withKind(ChannelKind.STATE);
201         return channelBuilder;
202     }
203
204     /**
205      * Get the {@link EnergyManagerHandler}.
206      *
207      * Only the {@link EnergyManagerHandler} has knowledge about the devices itself.
208      *
209      * @return instance responsible for this handler
210      */
211     protected @Nullable EnergyManagerHandler getEnergyManagerHandler() {
212         Bridge bridge = this.getBridge();
213         if (bridge != null) {
214             BridgeHandler bridgeHandler = bridge.getHandler();
215             if (bridgeHandler instanceof EnergyManagerHandler) {
216                 return (EnergyManagerHandler) bridgeHandler;
217             } else {
218                 // happens while dynamically reloading the binding
219                 this.logger.warn("BridgeHandler is not implementing EnergyManagerHandler {}", bridgeHandler);
220             }
221         } else {
222             // this handler can't work without a bridge
223             this.updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.OFFLINE.COMMUNICATION_ERROR,
224                     "Received null bridge while initializing!");
225         }
226
227         return null;
228     }
229
230     /**
231      * Get the {@link Device} from the {@link EnergyManagerHandler}.
232      *
233      * @return model with values
234      */
235     protected @Nullable Device getDevice() {
236         final EnergyManagerHandler bridgeHandler = this.getEnergyManagerHandler();
237
238         if (bridgeHandler != null) {
239             Map<String, Device> bridgeDevices = bridgeHandler.getDevices();
240             if (bridgeDevices != null) {
241                 return bridgeDevices.get(this.getConfigAs(SolarwattThingConfiguration.class).guid);
242             }
243         }
244
245         this.logger.warn("Device not found for thing with guid {}",
246                 this.getConfigAs(SolarwattThingConfiguration.class).guid);
247
248         return null;
249     }
250 }