001package jmri.jmrix.powerline.insteon2412s;
002
003import jmri.jmrix.powerline.InsteonSequence;
004import jmri.jmrix.powerline.SerialTrafficController;
005import jmri.util.StringUtil;
006import org.slf4j.Logger;
007import org.slf4j.LoggerFactory;
008
009/**
010 * Implementation of the Light Object for Insteon receivers on Insteon 2412S
011 * interfaces.
012 * <p>
013 * Uses X10 dimming commands to set intensity unless the value is 0.0 or 1.0, in
014 * which case it uses on/off commands only.
015 * <p>
016 * Since the dim/bright step of the hardware is unknown then the Light object is
017 * first created, the first time the intensity (not state) is set to other than
018 * 0.0 or 1.0, the output is run to it's maximum dim or bright step so that we
019 * know the count is right.
020 * <p>
021 * Keeps track of the controller's "dim count", and if not certain forces it to
022 * zero to be sure.
023 * <p>
024 *
025 *
026 * @author Dave Duchamp Copyright (C) 2004
027 * @author Bob Jacobsen Copyright (C) 2006, 2007, 2008, 2009, 2010
028 * @author Ken Cameron Copyright (C) 2009, 2010 Converted to multiple connection
029 * @author kcameron Copyright (C) 2011
030 */
031public class SpecificInsteonLight extends jmri.jmrix.powerline.SerialLight {
032
033    // System-dependent instance variables
034    /**
035     * Current output step 0 to maxDimStep.
036     * <p>
037     * -1 means unknown
038     */
039    int lastOutputStep = -1;
040
041    /**
042     * Largest Insteon dim step number available.
043     */
044    int maxDimStep = 255;
045
046    /**
047     * Value for retransmission
048     */
049    int maxHops = Constants.FLAG_MAXHOPS_DEFAULT;
050
051    public int getMaxHops() {
052        return maxHops;
053    }
054
055    public void setMaxHops(int maxHops) {
056        if (maxHops <= Constants.FLAG_MASK_MAXHOPS && maxHops >= 0) {
057            this.maxHops = maxHops;
058        } else {
059            log.error("setMaxHops out of range: {}", maxHops);
060        }
061    }
062
063    /**
064     * Create a Light object, with only system name.
065     * <p>
066     * 'systemName' was previously validated in SerialLightManager
067     * @param systemName text for systemName of light
068     * @param tc         tc for connection
069     */
070    public SpecificInsteonLight(String systemName, SerialTrafficController tc) {
071        super(systemName, tc);
072        this.tc = tc;
073        // maxDimStep = tc.getNumberOfIntensitySteps();
074    }
075
076    /**
077     * Create a Light object, with both system and user names.
078     * <p>
079     * 'systemName' was previously validated in SerialLightManager
080     * @param systemName text for systemName of light
081     * @param tc         tc for connection
082     * @param userName   text for userName of light
083     */
084    public SpecificInsteonLight(String systemName, SerialTrafficController tc, String userName) {
085        super(systemName, tc, userName);
086        this.tc = tc;
087        //maxDimStep = tc.getNumberOfIntensitySteps();
088    }
089
090    SerialTrafficController tc = null;
091
092    /**
093     * Invoked the first time intensity is set.
094     *
095     * @param intensity The next intensity value that will be set
096     */
097    @Override
098    protected void initIntensity(double intensity) {
099        if (log.isDebugEnabled()) {
100            log.debug("initIntensity({})", intensity);
101        }
102    }
103
104    /**
105     * Send a Dim/Bright command to the Insteon hardware to reach a specific
106     * intensity. Acts immediately, and changes no general state.
107     * <p>
108     * This sends "Dim" commands.
109     */
110    @Override
111    protected void sendIntensity(double intensity) {
112        if (log.isDebugEnabled()) {
113            log.debug("sendIntensity({}) lastOutputStep: {} maxDimStep: {}", intensity, lastOutputStep, maxDimStep);
114        }
115
116        // find the new correct dim count
117        int newStep = (int) Math.round(intensity * maxDimStep);  // maxDimStep is full on, 0 is full off, etc
118
119        // check for errors
120        if ((newStep < 0) || (newStep > maxDimStep)) {
121            log.error("newStep wrong: {} intensity: {}", newStep, intensity);
122        }
123
124        // do we have any change to make
125        if (newStep == lastOutputStep) {
126            // nothing to do!
127            if (log.isDebugEnabled()) {
128                log.debug("intensity {} within current step, return", intensity);
129            }
130            return;
131        }
132
133        if (log.isDebugEnabled()) {
134            log.debug("function set Intensity {}", intensity);
135        }
136
137        // create output sequence of address, then function
138        InsteonSequence out = new InsteonSequence();
139        out.addFunction(idhighbyte, idmiddlebyte, idlowbyte, Constants.FUNCTION_REQ_STD, (Constants.FLAG_STD | (maxHops << Constants.FLAG_SHIFT_HOPSLEFT) | maxHops), Constants.CMD_LIGHT_CHG, newStep);
140        // send
141        tc.sendInsteonSequence(out, null);
142
143        if (log.isDebugEnabled()) {
144            log.debug("sendIntensity({}) addr {}{}{} newStep {}", intensity, idhighbyte, idmiddlebyte, idlowbyte, newStep);
145        }
146
147        lastOutputStep = newStep;
148    }
149
150    /**
151     * Number of steps from dim to bright is maintained in specific
152     * SerialTrafficController implementation
153     */
154    @Override
155    protected int getNumberOfSteps() {
156        return maxDimStep;
157    }
158
159    /**
160     * Send a On/Off Command to the hardware
161     */
162    @Override
163    protected void sendOnOffCommand(int newState) {
164        if (log.isDebugEnabled()) {
165            log.debug("start sendOnOff({}) Current: {}", newState, mState);
166        }
167
168        // figure out command 
169        int command1;
170        if (newState == ON) {
171            command1 = Constants.CMD_LIGHT_ON_FAST;
172        } else if (newState == OFF) {
173            command1 = Constants.CMD_LIGHT_OFF_FAST;
174        } else {
175            log.warn("illegal state requested for Light: {}", getSystemName());
176            return;
177        }
178
179        if (log.isDebugEnabled()) {
180            log.debug("set state {} {}", newState, getSystemName());
181        }
182
183        // create output sequence of just address and function together
184        InsteonSequence out = new InsteonSequence();
185        out.addFunction(idhighbyte, idmiddlebyte, idlowbyte, Constants.FUNCTION_REQ_STD, (Constants.FLAG_STD | (maxHops << Constants.FLAG_SHIFT_HOPSLEFT) | maxHops), command1, 0);
186        // send
187        tc.sendInsteonSequence(out, null);
188
189        if (log.isDebugEnabled()) {
190            log.debug("end sendOnOff({})  insteon {}.{}.{} cmd1: {}", newState, StringUtil.twoHexFromInt(idhighbyte), StringUtil.twoHexFromInt(idmiddlebyte), StringUtil.twoHexFromInt(idlowbyte), command1);
191        }
192    }
193
194    private final static Logger log = LoggerFactory.getLogger(SpecificInsteonLight.class);
195}