/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.withings.internal; import java.util.Collection; import java.util.Dictionary; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.lang.StringUtils; import org.openhab.binding.withings.WithingsBindingConfig; import org.openhab.binding.withings.WithingsBindingProvider; import org.openhab.binding.withings.internal.api.WithingsApiClient; import org.openhab.binding.withings.internal.model.Category; import org.openhab.binding.withings.internal.model.Measure; import org.openhab.binding.withings.internal.model.MeasureGroup; import org.openhab.binding.withings.internal.model.MeasureType; import org.openhab.core.binding.AbstractActiveBinding; import org.openhab.core.binding.BindingProvider; import org.openhab.core.library.types.DecimalType; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link WithingsBinding} polls the Withings API in a defined * {@link WithingsBinding#refreshInterval} and updates all items with a * {@link WithingsBindingConfig}. * * @author Dennis Nobel * @since 1.5.0 */ public class WithingsBinding extends AbstractActiveBinding<WithingsBindingProvider>implements ManagedService { private static final Logger logger = LoggerFactory.getLogger(WithingsBinding.class); /** * Holds the time of last update. */ private int lastUpdate = 0; /** * The refresh interval which is used to poll values from the Withings * server (optional, defaults to 3600000 ms) */ private long refreshInterval = 3600000; private final List<WithingsApiClient> withingsApiClients = new CopyOnWriteArrayList<WithingsApiClient>(); @Override protected String getName() { return "Withings Refresh Service"; } @Override protected long getRefreshInterval() { return refreshInterval; } @Override public void allBindingsChanged(BindingProvider provider) { super.allBindingsChanged(provider); if (isProperlyConfigured()) { execute(); } } @Override protected synchronized void execute() { Map<String, WithingsBindingConfig> withingsBindings = getWithingsBindings(); if (withingsBindings.isEmpty()) { logger.info("No item -> withings binding found. Skipping data refresh."); return; } if (this.withingsApiClients.isEmpty()) { logger.info( "No withings client found. Withings binding is probably not authenticated. Skipping data refresh."); return; } updateItemStates(withingsBindings); } private Float findLastMeasureValue(List<MeasureGroup> measures, MeasureType measureType) { for (MeasureGroup measureGroup : measures) { if (measureGroup.category == Category.MEASURE) { for (Measure measure : measureGroup.measures) { if (measure.type == measureType) { return measure.getActualValue(); } } } } return null; } private Map<String, WithingsBindingConfig> getWithingsBindings() { Map<String, WithingsBindingConfig> bindings = new HashMap<String, WithingsBindingConfig>(); for (WithingsBindingProvider provider : this.providers) { Collection<String> itemNames = provider.getItemNames(); for (String itemName : itemNames) { WithingsBindingConfig config = provider.getItemConfig(itemName); bindings.put(itemName, config); } } return bindings; } private int now() { return (int) (System.currentTimeMillis() / 1000); } private void updateItemState(String itemName, WithingsBindingConfig withingsBindingConfig, List<MeasureGroup> measures) { MeasureType measureType = withingsBindingConfig.measureType; Float lastMeasureValue = findLastMeasureValue(measures, measureType); if (lastMeasureValue != null) { eventPublisher.postUpdate(itemName, new DecimalType(lastMeasureValue)); } } private void updateItemStates(Map<String, WithingsBindingConfig> withingsBindings) { try { WithingsApiClient client = this.withingsApiClients.get(0); List<MeasureGroup> measures = client.getMeasures(lastUpdate); if (measures == null || measures.isEmpty()) { logger.info("No new measures found since the last update."); return; } for (Entry<String, WithingsBindingConfig> withingBinding : withingsBindings.entrySet()) { WithingsBindingConfig withingsBindingConfig = withingBinding.getValue(); String itemName = withingBinding.getKey(); updateItemState(itemName, withingsBindingConfig, measures); } lastUpdate = now(); } catch (Exception ex) { logger.error("Cannot get Withings measure data: " + ex.getMessage(), ex); } } protected void addWithingsApiClient(WithingsApiClient withingsApiClient) { this.withingsApiClients.add(withingsApiClient); if (!isProperlyConfigured()) { setProperlyConfigured(true); } } protected void removeWithingsApiClient(WithingsApiClient withingsApiClient) { this.withingsApiClients.remove(withingsApiClient); if (withingsApiClients.isEmpty()) { setProperlyConfigured(false); } } protected void addBindingProvider(WithingsBindingProvider bindingProvider) { super.addBindingProvider(bindingProvider); } protected void removeBindingProvider(WithingsBindingProvider bindingProvider) { super.removeBindingProvider(bindingProvider); } @Override public void updated(Dictionary<String, ?> config) throws ConfigurationException { if (config != null) { String refreshInterval = (String) config.get("refresh"); if (StringUtils.isNotBlank(refreshInterval)) { this.refreshInterval = Long.parseLong(refreshInterval); restartPollingThread(); } } } private void restartPollingThread() { if (isProperlyConfigured() && activeService.isRunning()) { activeService.shutdown(); activeService.interrupt(); try { // wait 5 seconds until polling thread is definitely // shutdown Thread.sleep(5000); } catch (InterruptedException unhandled) { } setProperlyConfigured(isProperlyConfigured()); } } }