All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ogema.drivers.homematic.xmlrpc.hl.HomeMaticDriver Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2011-2018 Fraunhofer-Gesellschaft zur Förderung der angewandten Wissenschaften e.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ogema.drivers.homematic.xmlrpc.hl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.xmlrpc.XmlRpcException;
import org.ogema.core.application.Application;
import org.ogema.core.application.ApplicationManager;
import org.ogema.core.application.Timer;
import org.ogema.core.application.TimerListener;
import org.ogema.core.model.Resource;
import org.ogema.core.model.simple.BooleanResource;
import org.ogema.core.model.simple.FloatResource;
import org.ogema.core.model.simple.IntegerResource;
import org.ogema.core.model.simple.SingleValueResource;
import org.ogema.core.model.simple.StringResource;
import org.ogema.core.resourcemanager.ResourceDemandListener;
import org.ogema.drivers.homematic.xmlrpc.hl.api.DeviceHandler;
import org.ogema.drivers.homematic.xmlrpc.hl.api.DeviceHandlerFactory;
import org.ogema.drivers.homematic.xmlrpc.hl.types.HmDevice;
import org.ogema.drivers.homematic.xmlrpc.hl.types.HmLogicInterface;
import org.ogema.drivers.homematic.xmlrpc.ll.api.DeviceDescription;
import org.ogema.drivers.homematic.xmlrpc.ll.api.HmEvent;
import org.ogema.drivers.homematic.xmlrpc.ll.api.ParameterDescription;
import org.osgi.framework.Constants;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.event.EventAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author jlapp
 */
@Component(service = Application.class)
public class HomeMaticDriver implements Application {

    private ApplicationManager appman;
    private EventAdmin eventAdmin;
    private ComponentContext ctx;
    private Logger logger = LoggerFactory.getLogger(getClass());
    private final Map connections = new HashMap<>();

    private final SortedSet handlerFactories = new TreeSet<>();
   
    // store accepted devices (by address) so they are not offered again on a different connection
    private final Map> acceptedDevices = new HashMap<>();

    private static class HandlerRegistration implements Comparable {

        DeviceHandlerFactory fac;
        int ranking;

        public HandlerRegistration(DeviceHandlerFactory fac, int ranking) {
            this.fac = fac;
            this.ranking = ranking;
        }

        @Override
        public int compareTo(HandlerRegistration o) {
            int rankCompare = Integer.compare(ranking, o.ranking);
            return rankCompare == 0 ? o.fac.getClass().getCanonicalName().compareTo(fac.getClass().getCanonicalName())
                    : - rankCompare;
        }

        @Override
        public boolean equals(Object obj) {
            return (obj instanceof HandlerRegistration) && fac.getClass() == ((HandlerRegistration) obj).fac.getClass();
        }

        @Override
        public int hashCode() {
            return Objects.hashCode(fac);
        }

        @Override
        public String toString() {
            return String.format("%d: %s", ranking, fac.getClass().getCanonicalName());
        }
        
    }
    
    // this is a static + greedy reference so that external handler factories
    // can always replace the built-in handlers.
    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policyOption = ReferencePolicyOption.GREEDY)
    protected void addHandlerFactory(DeviceHandlerFactory fac, Map serviceProperties) {
        int ranking = 1;
        if (serviceProperties.containsKey(Constants.SERVICE_RANKING)) {
            ranking = (int) serviceProperties.get(Constants.SERVICE_RANKING);
        }
        logger.info("adding handler factory {}, rank {}", fac, ranking);
        synchronized (handlerFactories) {
            handlerFactories.add(new HandlerRegistration(fac, ranking));
        }
    }
    
    protected void removeHandlerFactory(DeviceHandlerFactory fac, Map serviceProperties) {
        // nothing to do for static reference handling
    }

    @Activate
    protected void activate(ComponentContext ctx) {
        this.ctx = ctx;
    }

    final ResourceDemandListener configListener = new ResourceDemandListener() {

        @Override
        public void resourceAvailable(HmLogicInterface t) {
            List l = new ArrayList<>(handlerFactories.size());
            synchronized (handlerFactories) {
                for (HandlerRegistration reg: handlerFactories) {
                    l.add(reg.fac);
                }
            }
            HmConnection conn = new HmConnection(l, appman, eventAdmin, ctx, HomeMaticDriver.this, t);
            connections.put(t, conn);
            conn.init();
        }

        @Override
        public void resourceUnavailable(HmLogicInterface t) {
            connections.remove(t).close();
        }

    };
    

    @Override
    public void start(ApplicationManager am) {
        appman = am;
        logger = am.getLogger();
        // delay actual setup so that all external ChannelHandlerFactories
        // are available (cosmetic change, quick restarts seem to work fine)
        final Timer t = appman.createTimer(2000);
        t.addListener(new TimerListener() {
            @Override
            public void timerElapsed(Timer timer) {
            	timer.destroy();
                logger.info("HomeMatic driver ready, configuration pending");
                appman.getResourceAccess().addResourceDemand(HmLogicInterface.class, configListener);
            }
        });
    }

    @Override
    public void stop(AppStopReason asr) {
        if (appman != null) {
            appman.getResourceAccess().removeResourceDemand(HmLogicInterface.class, configListener);
        }
    	final Iterator it = connections.values().iterator();
    	while (it.hasNext()) {
    		final HmConnection conn = it.next();
    		it.remove();
    		conn.close();
    	}
    	acceptedDevices.clear();
    }
    
    protected void pollParameters(HmConnection connection) {
        for (HmDevice dev : connection.baseResource.devices().getAllElements()) {
            for (HmDevice sub : dev.channels().getAllElements()) {
                setupDevice(sub);
            }
        }
    }

    protected HmConnection findConnection(HmDevice dev) {
        Resource p = dev;
        while (p.getParent() != null) {
            p = p.getParent();
        }
        if (!(p instanceof HmLogicInterface)) {
            throw new IllegalStateException("HmDevice in wrong place: " + dev.getPath());
        }
        return connections.get((HmLogicInterface) p);
    }

    public void setupDevice(HmDevice dev) {
        String address = dev.address().getValue();
        if (acceptedDevices.containsKey(address)) {
            logger.debug("device {} already controlled by handler type {}", address, acceptedDevices.get(address));
            return;
        }
        HmConnection conn = findConnection(dev);
        if (conn == null) {
            logger.warn("no connection for device {}", dev.getPath());
            return;
        }
        try {
            DeviceDescription channelDesc = conn.client.getDeviceDescription(address);
            if (channelDesc.isDevice()) {
                dev.addStructureListener(new DeletionListener(address, conn.client, true, logger));
            }
            for (DeviceHandler h : conn.handlers) {
                if (h.accept(channelDesc)) {
                    logger.debug("handler available for {}: {}", address, h.getClass().getCanonicalName());
                    Map>> paramSets = new HashMap<>();
                    for (String set : dev.paramsets().getValues()) {
                        logger.trace("requesting paramset {} of device {}", set, address);
                        paramSets.put(set, conn.client.getParamsetDescription(address, set));
                    }
                    HmDevice masterDevice = channelDesc.isDevice() ?
                            dev : (HmDevice) dev.getParent().getParent();
                    h.setup(masterDevice, channelDesc, paramSets);
                    acceptedDevices.put(address, h.getClass().asSubclass(DeviceHandler.class));
                    break;
                }
            }
        } catch (XmlRpcException ex) {
            logger.debug("failed to configure value resources for device " + dev.getPath()+ " address:"+address, ex);
        }
    }

    public void storeEvent(HmEvent e, SingleValueResource res) {
        logger.debug("storing event data for {}@{} to {}", e.getValueKey(), e.getAddress(), res.getPath());
        if (res instanceof FloatResource) {
            ((FloatResource) res).setValue(e.getValueFloat());
        } else if (res instanceof IntegerResource) {
            ((IntegerResource) res).setValue(e.getValueInt());
        } else if (res instanceof StringResource) {
            ((StringResource) res).setValue(e.getValueString());
        } else if (res instanceof BooleanResource) {
            ((BooleanResource) res).setValue(e.getValueBoolean());
        } else {
            logger.warn("HomeMatic parameter resource is of unsupported type: {}", res.getResourceType());
        }
    }

    @Reference
    public void setEventAdmin(EventAdmin eventAdmin) {
        this.eventAdmin = eventAdmin;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy