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