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

com.github.besherman.lifx.impl.light.LFXLightImpl 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.LFXAlarmCollection;
import com.github.besherman.lifx.LFXLight;
import com.github.besherman.lifx.LFXHSBKColor;
import com.github.besherman.lifx.LFXLightDetails;
import com.github.besherman.lifx.impl.entities.LFXPowerState;
import com.github.besherman.lifx.impl.entities.internal.LFXBinaryTypes;
import com.github.besherman.lifx.impl.entities.internal.LFXDeviceID;
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 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 com.github.besherman.lifx.impl.network.LFXTimerQueue;
import java.awt.Color;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

public class LFXLightImpl implements LFXLight {
    private final LFXMessageRouter router;    
    private final LFXDeviceID deviceID;    
    private final LFXTarget target;    
    private final LFXLightDetailsImpl details;
    private final LFXAlarmCollectionImpl alarms;
    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);    
    private final int lightLostTimeout;
    
    private boolean enabled;
    private String label = "";
    private LFXHSBKColor color;
    private Date time;        
    private long lastSeenTimestamp;

    // the messages we wait for unit we call this light loaded
    private final Set messagesUntilLoaded = Collections.synchronizedSet(new HashSet<>(Arrays.asList(
            // wait for the basic information is loaded
            LxProtocol.Type.LX_PROTOCOL_LIGHT_STATE,
            LxProtocol.Type.LX_PROTOCOL_DEVICE_STATE_LABEL,
            LxProtocol.Type.LX_PROTOCOL_DEVICE_STATE_POWER,
            LxProtocol.Type.LX_PROTOCOL_DEVICE_STATE_TIME
    )));
    
    public LFXLightImpl(LFXMessageRouter router, LFXTimerQueue timerQueue, LFXDeviceID deviceID) {
        this.target = new LFXTarget(deviceID);
        this.deviceID = deviceID;
        this.router = router;        
        this.alarms = new LFXAlarmCollectionImpl(router, new LFXTarget(deviceID));
        this.details = new LFXLightDetailsImpl(router, new LFXTarget(deviceID));        
        this.lightLostTimeout = Integer.parseInt(System.getProperty("com.github.besherman.lifx.dh.lightLostTimeout", "20000"));
    }

    @Override
    public String getID() {
        return deviceID.getStringRepresentation();
    }
    
    public LFXDeviceID getDeviceID() {
        return deviceID;
    }
    
    @Override
    public String getLabel() {
        return label;
    }

    @Override
    public boolean isLabelAllowed(String newLabel) {
        if(newLabel == null) {
            return false;
        }
        try {
            return newLabel.getBytes("UTF-8").length <= 32;
        } catch(UnsupportedEncodingException ex) {
            throw new InternalError();
        }
    }       

    @Override
    public void setLabel(String label) {
        if(!isLabelAllowed(label)) {
            throw new IllegalArgumentException("invalid label");
        }
        
        char[] arr = label.toCharArray();
        label = new String(arr, 0, Math.min(arr.length, 32));
        
        LxProtocolDevice.SetLabel payload = new LxProtocolDevice.SetLabel(label);
        LFXMessage message = new LFXMessage(LxProtocol.Type.LX_PROTOCOL_DEVICE_SET_LABEL, target, payload);                
        for(int i = 0; i < 3; i++) {
            router.sendMessage(message);
        }        
        
        labelDidChangeTo(label);
    }

    @Override
    public LFXHSBKColor getColor() {
        return color;
    }

    @Override
    public void setColor(Color color) {
        setColor(new LFXHSBKColor(color));
    }

    @Override
    public void setColor(Color color, long duration) {
        setColor(new LFXHSBKColor(color), duration);
    }

    @Override
    public void setColor(LFXHSBKColor color) {
        setColor(color, 250);
    }    
    
    @Override
    public void setColor(LFXHSBKColor color, long duration) {
        if(color == null) {
            throw new IllegalArgumentException("color can not be null");
        }
        
        StructleTypes.UInt8 stream = new StructleTypes.UInt8(0);
        LxProtocolLight.Hsbk protocolColor = LFXBinaryTypes.getLXProtocolLightHsbkFromLFXHSBKColor(color);
        StructleTypes.UInt32 protocolDuration = new StructleTypes.UInt32(duration);
        LxProtocolLight.Set payload = new LxProtocolLight.Set(stream, protocolColor, protocolDuration);
        LFXMessage message = new LFXMessage(LxProtocol.Type.LX_PROTOCOL_LIGHT_SET, target, payload);        
        
        router.sendMessage(message);
        
        colorDidChangeTo(color);        
    }    

    @Override
    public boolean isPower() {
        return enabled;
    }

    @Override
    public void setPower(boolean power) {
        LFXPowerState state = power ? LFXPowerState.ON : LFXPowerState.OFF;
        StructleTypes.UInt16 protocolPowerLevel = LFXBinaryTypes.getLFXProtocolPowerLevelFromLFXPowerState(state);
        LxProtocolDevice.SetPower payload = new LxProtocolDevice.SetPower(protocolPowerLevel);
        LFXMessage message = new LFXMessage(LxProtocol.Type.LX_PROTOCOL_DEVICE_SET_POWER, target, payload);
        
        router.sendMessage(message);
        router.sendMessage(message);
        
        powerDidChangeTo(state);
    }

    @Override
    public LFXAlarmCollection getAlarms() {
        return alarms;
    }

    @Override
    public Date getTime() {
        return new Date();
    }

    @Override
    public LFXLightDetails getDetails() {
        return details;
    }

    @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 boolean isLoaded() {
        return messagesUntilLoaded.isEmpty() && alarms.isLoaded() && details.isLoaded();
    }
    
    /**
     * Returns the message types this light is waiting for before it can call
     * itself loaded. 
     * 
     * TODO: remove this
     */
    public Set getMessagesUntilLoaded() {
        return this.messagesUntilLoaded;
    }
        
    public LFXTarget getTarget() {
        return target;
    }

    @Override
    public String toString() {
        return "LFXLightImpl{" + "deviceID=" + deviceID + '}';
    }
    
    public boolean isLost() {
        return (System.currentTimeMillis() - lastSeenTimestamp) > lightLostTimeout;
    }
    
    public void close() {
        details.close();
        alarms.close();
    }
    
    public void handleMessage(LFXMessage message) {
        lastSeenTimestamp = System.currentTimeMillis();
        switch (message.getType()) {
            case LX_PROTOCOL_LIGHT_STATE: {
                LxProtocolLight.State payload = (LxProtocolLight.State) message.getPayload();
                labelDidChangeTo(payload.getLabel());
                colorDidChangeTo(LFXBinaryTypes.getLFXHSBKColorFromLXProtocolLightHsbk(payload.getColor()));
                powerDidChangeTo(LFXBinaryTypes.getLFXPowerStateFromLFXProtocolPowerLevel(payload.getPower()));
                break;
            }
            case LX_PROTOCOL_DEVICE_STATE_LABEL: {
                LxProtocolDevice.StateLabel payload = (LxProtocolDevice.StateLabel) message.getPayload();
                labelDidChangeTo(payload.getLabel());
                break;
            }
            case LX_PROTOCOL_DEVICE_STATE_POWER: {
                LxProtocolDevice.StatePower payload = (LxProtocolDevice.StatePower) message.getPayload();
                powerDidChangeTo(LFXBinaryTypes.getLFXPowerStateFromLFXProtocolPowerLevel(payload.getLevel()));
                break;
            }
            case LX_PROTOCOL_DEVICE_STATE_TIME: {
                LxProtocolDevice.StateTime payload = message.getPayload();
                timeDidChangeTo(payload.getTime().getBigIntegerValue());
                break;
            }
            case LX_PROTOCOL_DEVICE_STATE_INFO: {
                // The light handler does not send this automatically, but when
                // the LFXLightDetailsImpl sends it we can read it 
                LxProtocolDevice.StateInfo payload = message.getPayload();                                
                timeDidChangeTo(payload.getTime().getBigIntegerValue());
                break;
            }
            default:
                break;
        }
        
        alarms.handleMessage(message);
        details.handleMessage(message);
        
        messagesUntilLoaded.remove(message.getType());
    }    
    
    private void labelDidChangeTo(String newLabel) {
        String oldLabel = label;
        label = newLabel;
        pcs.firePropertyChange("label", oldLabel, newLabel);
    }

    public void colorDidChangeTo(LFXHSBKColor newColor) {
        LFXHSBKColor oldColor = color;
        color = newColor;
        pcs.firePropertyChange("color", oldColor, newColor);        
    }

    public void powerDidChangeTo(LFXPowerState powerState) {
        boolean oldValue = enabled;
        this.enabled = (powerState == powerState.ON);
        pcs.firePropertyChange("power", oldValue, enabled);
    }
    
    private void timeDidChangeTo(BigInteger newTime) {
        Date oldTime = time;
        long msSinceEpoch = newTime.longValue() / 1000000;
        time = new Date(msSinceEpoch);        
        pcs.firePropertyChange("time", oldTime, time);
    }    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy