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

com.github.besherman.lifx.impl.light.LFXAlarmCollectionImpl Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 *
 * Copyright 2014 Richard Löfberg.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.github.besherman.lifx.impl.light;

import com.github.besherman.lifx.LFXAlarm;
import com.github.besherman.lifx.LFXAlarmCollection;
import com.github.besherman.lifx.LFXHSBKColor;
import com.github.besherman.lifx.LFXInterfaceFirmware;
import com.github.besherman.lifx.LFXWaveform;
import com.github.besherman.lifx.impl.entities.internal.LFXBinaryTypes;
import com.github.besherman.lifx.impl.entities.internal.LFXByteUtils;
import com.github.besherman.lifx.impl.entities.internal.LFXMessage;
import com.github.besherman.lifx.impl.entities.internal.LFXTarget;
import com.github.besherman.lifx.impl.entities.internal.structle.LxProtocol;
import static com.github.besherman.lifx.impl.entities.internal.structle.LxProtocol.Type.LX_PROTOCOL_DEVICE_STATE_WIFI_FIRMWARE;
import static com.github.besherman.lifx.impl.entities.internal.structle.LxProtocol.Type.LX_PROTOCOL_LIGHT_SET_SIMPLE_EVENT;
import static com.github.besherman.lifx.impl.entities.internal.structle.LxProtocol.Type.LX_PROTOCOL_LIGHT_STATE_SIMPLE_EVENT;
import com.github.besherman.lifx.impl.entities.internal.structle.LxProtocolDevice;
import com.github.besherman.lifx.impl.entities.internal.structle.LxProtocolLight;
import com.github.besherman.lifx.impl.entities.internal.structle.StructleTypes;
import com.github.besherman.lifx.impl.network.LFXMessageRouter;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The protocol specifies 256 possible alarms, but it also has a max flag that
 * is sent with each message which tells us how many alarms the light can 
 * use. 
 */
public class LFXAlarmCollectionImpl implements LFXAlarmCollection {
    private final LFXTarget target;
    private final LFXMessageRouter router;
    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    
    private final List alarms = Collections.synchronizedList(new ArrayList());    
    
    private final AtomicBoolean closed = new AtomicBoolean(false);
    
    private final AtomicBoolean clearForInit = new AtomicBoolean(false);
    
    
    public LFXAlarmCollectionImpl(LFXMessageRouter router, LFXTarget target) {
        this.target = target;
        this.router = router;        
    }

    @Override
    public int size() {
        return alarms.size();
    }

    @Override
    public Iterator iterator() {
        return Collections.unmodifiableList(alarms).iterator();
    }

    @Override
    public LFXAlarm get(int index) {
        synchronized(alarms) {
            if(index < 0 || index >= size()) {
                throw new IndexOutOfBoundsException();
            }
            return alarms.get(index);
        }
    }

    @Override
    public void set(int index, LFXAlarm alarm) {
        LFXAlarm old;
        synchronized(alarms) {
            if(index < 0 || index >= size()) {
                throw new IndexOutOfBoundsException();
            }

            old = alarms.get(index);
            alarms.set(index, alarm);
        }
        
        pcs.fireIndexedPropertyChange("alarms", index, old, alarm);        
        sendSetAlarm(index, alarm);
    }

    @Override
    public void clear(int index) {
        LFXAlarm old;
        LFXAlarm cleared = createEmptyAlarm();
        synchronized(alarms) {
            if(index < 0 || index >= size()) {
                throw new IndexOutOfBoundsException();
            }
            
            old = alarms.get(index);
            alarms.set(index, cleared);
        }
        
        pcs.fireIndexedPropertyChange("alarms", index, old, cleared);        
        sendSetAlarm(index, cleared);
    }
    

    @Override
    public void load() {
        if(closed.get()) {            
            return;
        }
        
        if(!clearForInit.get()) {
            return;
        }                

        // TODO: this should be revisited when lights supports more than two
        // alarms.
        sendGetAlarm(0);        
        sendGetAlarm(1);        
    }

    @Override
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }

    @Override
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
        pcs.addPropertyChangeListener(propertyName, l);
    }

    @Override
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }
    
    public void close() {        
        closed.set(false);
    }    
    
    public boolean isLoaded() {
        // TODO implement this
        return true;
    }    

    public void handleMessage(LFXMessage message) {
        if(message.getType() == LX_PROTOCOL_LIGHT_STATE_SIMPLE_EVENT) {
            LxProtocolLight.StateSimpleEvent payload = message.getPayload();
            String hex = LFXByteUtils.byteArrayToHexString(payload.getBytes());
            
            simpleEventDidChangeTo(payload);
        } else if(message.getType() == LX_PROTOCOL_DEVICE_STATE_WIFI_FIRMWARE) {
            if(!clearForInit.get()) {
                LxProtocolDevice.StateWifiFirmware payload = message.getPayload();
                LFXInterfaceFirmware wifi = LFXBinaryTypes.createFirmware(payload.getBuild(), payload.getInstall(), payload.getVersion());
                boolean lightSupportsAlarms = wifi.getMajorVersion() > 1 || wifi.getMajorVersion() == 1 && wifi.getMinorVersion() >= 5;
                if(lightSupportsAlarms) {
                    clearForInit.set(true);
                    load();
                } else {
                    Logger.getLogger(LFXAlarmCollectionImpl.class.getName()).log(Level.INFO, 
                            String.format("light %s has firmware version %s which does not support alarms", 
                                    target.getDeviceID().getStringRepresentation(),
                                    wifi.getVersion()));
                }
            }
        }
    }
    
    
    private static LFXAlarm createEmptyAlarm() {
        LFXHSBKColor emptyColor = new LFXHSBKColor(0, 0, 0, 0);
        LFXWaveform emptyWaveform = new LFXWaveform(0, false, emptyColor, 0, 0, (short)0, 0);
        Date emptyDate = new Date(0);        
        return new LFXAlarm(emptyDate, false, 0, emptyWaveform);        
    }    
    
    private void sendSetAlarm(int index, LFXAlarm alarm) {
        LxProtocolLight.Waveform waveform;
        {
            StructleTypes.UInt8 stream = new StructleTypes.UInt8(alarm.getWaveform().getStream());
            StructleTypes.Bool8 transienttype = new StructleTypes.Bool8(alarm.getWaveform().isTransientType());
            LxProtocolLight.Hsbk color = LFXBinaryTypes.getLXProtocolLightHsbkFromLFXHSBKColor(alarm.getWaveform().getColor());
            StructleTypes.UInt32 period = new StructleTypes.UInt32(alarm.getWaveform().getPeriod());
            StructleTypes.Float32 cycles = new StructleTypes.Float32(alarm.getWaveform().getCycles());
            StructleTypes.Int16 skrew_ratio = new StructleTypes.Int16(alarm.getWaveform().getSkewRatio());
            StructleTypes.UInt8 wf = new StructleTypes.UInt8(alarm.getWaveform().getWaveform());            
            
            waveform = new LxProtocolLight.Waveform(stream, transienttype, color, period, cycles, skrew_ratio, wf);
        }
        
        StructleTypes.UInt8 idx = new StructleTypes.UInt8(index);
        BigInteger t = BigInteger.valueOf(alarm.getTime().getTime());
        t = t.multiply(new BigInteger("1000000"));
        StructleTypes.UInt64 time = new StructleTypes.UInt64(t);
        StructleTypes.UInt16 power = new StructleTypes.UInt16(alarm.isPower() ? 1 : 0);
        StructleTypes.UInt32 duration = new StructleTypes.UInt32(alarm.getDuration());
        LxProtocolLight.SetSimpleEvent payload = new LxProtocolLight.SetSimpleEvent(idx, time, power, duration, waveform);
        
        for(int i = 0; i < 3; i++) {
            router.sendMessage(new LFXMessage(LX_PROTOCOL_LIGHT_SET_SIMPLE_EVENT, target, payload));
        }
    }
    
    private void simpleEventDidChangeTo(LxProtocolLight.StateSimpleEvent payload) {        
        int index = payload.getIndex().getValue();
        
        synchronized(alarms) {
            int maxNumberOfAlarms = payload.getMax().getValue();
            while(maxNumberOfAlarms > alarms.size()) {
                alarms.add(createEmptyAlarm());
            }
        }
        
        if(index < 0 || index >= alarms.size()) {
            Logger.getLogger(LFXAlarmCollectionImpl.class.getName()).log(Level.SEVERE, 
                    "Received alarm with index out of range. Index was {0} size is {1}", 
                    new Object[]{index, alarms.size()});
            return;
        }
        
        LxProtocolLight.Waveform wf = payload.getWaveform();        
        LFXWaveform waveform = new LFXWaveform(wf.getStream().getValue(), 
                wf.getTransienttype().getValue(), 
                LFXBinaryTypes.getLFXHSBKColorFromLXProtocolLightHsbk(wf.getColor()), 
                wf.getPeriod().getValue(), 
                wf.getCycles().getValue(), 
                (short)wf.getSkewRatio().getValue(), 
                wf.getWaveform().getValue()); 
        
        BigInteger time = payload.getTime().getBigIntegerValue();
        time = time.divide(new BigInteger("1000000"));
        Date date = new Date(time.longValue());
        
        LFXAlarm alarm = new LFXAlarm(date, 
                payload.getPower().getValue() > 0, 
                payload.getDuration().getValue(), 
                waveform);        

        
        LFXAlarm old;                
        synchronized(alarms) {
            old = alarms.get(index);
            alarms.set(index, alarm);     
        }   
         
        pcs.fireIndexedPropertyChange("alarms", index, old, alarm);        
    }   
   
    
    private void sendGetAlarm(int index) {         
        LxProtocolLight.GetSimpleEvent payload = new LxProtocolLight.GetSimpleEvent(new StructleTypes.UInt8(index));
        LFXMessage msg = new LFXMessage(LxProtocol.Type.LX_PROTOCOL_LIGHT_GET_SIMPLE_EVENT, target, payload);
        router.sendMessage(msg);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy