001package jmri.jmrix.powerline;
002
003import org.slf4j.Logger;
004import org.slf4j.LoggerFactory;
005
006/**
007 * Implementation of the Light class for X10-based subclasses.
008 * <p>
009 * Uses X10 dimming commands to set intensity unless the value is 0.0 or 1.0, in
010 * which case it uses on/off commands only.
011 * <p>
012 * Since the dim/bright step of the hardware is unknown then the Light object is
013 * first created, the first time the intensity (not state) is set to other than
014 * 0.0 or 1.0, the output is run to it's maximum dim or bright step so that we
015 * know the count is right.
016 * <p>
017 * Keeps track of the controller's "dim count", and if not certain forces it to
018 * zero to be sure.
019 * <p>
020 *
021 * @author Dave Duchamp Copyright (C) 2004
022 * @author Bob Jacobsen Copyright (C) 2006, 2007, 2008, 2009, 2010
023 * @author Ken Cameron Copyright (C) 2009, 2010 Converted to multiple connection
024 * @author kcameron Copyright (C) 2011
025 */
026public class SerialX10Light extends jmri.jmrix.powerline.SerialLight {
027
028    // System-dependent instance variables
029    /**
030     * Current output step 0 to maxDimStep.
031     * <p>
032     * -1 means unknown
033     */
034    protected int lastOutputStep = -1;
035
036    /**
037     * Largest X10 dim step number available.
038     * <p>
039     * Loaded from SerialTrafficController.getNumberOfIntensitySteps();
040     */
041    protected int maxDimStep = 0;
042
043    /**
044     * Create a Light object, with only system name.
045     * <p>
046     * 'systemName' was previously validated in SerialLightManager
047     * @param systemName system name
048     * @param tc traffic controller
049     */
050    public SerialX10Light(String systemName, SerialTrafficController tc) {
051        super(systemName, tc);
052        this.tc = tc;
053        maxDimStep = tc.getNumberOfIntensitySteps();
054    }
055
056    /**
057     * Create a Light object, with both system and user names.
058     * <p>
059     * 'systemName' was previously validated in SerialLightManager
060     * @param systemName system name
061     * @param tc traffic controller
062     * @param userName user name
063     */
064    public SerialX10Light(String systemName, SerialTrafficController tc, String userName) {
065        super(systemName, tc, userName);
066        this.tc = tc;
067        maxDimStep = tc.getNumberOfIntensitySteps();
068    }
069
070    SerialTrafficController tc = null;
071
072    /**
073     * Optionally, force control to a known "dim count".
074     * <p>
075     * Invoked the first time intensity is set.
076     *
077     * @param intensity The next intensity value that will be set
078     */
079    @Override
080    protected void initIntensity(double intensity) {
081        if (log.isDebugEnabled()) {
082            log.debug("initIntensity({})", intensity);
083        }
084
085        maxDimStep = tc.getNumberOfIntensitySteps();
086
087        // Set initial state
088        // see if going to stabilize at on or off
089        if (intensity <= 0.5) {
090            // going to low, send a real off
091            X10Sequence out3 = new X10Sequence();
092            out3.addAddress(housecode, devicecode);
093            out3.addFunction(housecode, X10Sequence.FUNCTION_OFF, 0);
094            tc.sendX10Sequence(out3, null);
095            // going to low, send max dim count low
096            X10Sequence out2 = new X10Sequence();
097            out2.addAddress(housecode, devicecode);
098            out2.addFunction(housecode, X10Sequence.FUNCTION_DIM, maxDimStep);
099            tc.sendX10Sequence(out2, null);
100
101            lastOutputStep = 0;
102
103            if (log.isDebugEnabled()) {
104                log.debug("initIntensity: sent dim reset");
105            }
106        } else {
107            // going to high, send a real on
108            X10Sequence out3 = new X10Sequence();
109            out3.addAddress(housecode, devicecode);
110            out3.addFunction(housecode, X10Sequence.FUNCTION_ON, 0);
111            tc.sendX10Sequence(out3, null);
112            // going to high, send max dim count high
113            X10Sequence out2 = new X10Sequence();
114            out2.addAddress(housecode, devicecode);
115            out2.addFunction(housecode, X10Sequence.FUNCTION_BRIGHT, maxDimStep);
116            // send
117            tc.sendX10Sequence(out2, null);
118
119            lastOutputStep = maxDimStep;
120
121            if (log.isDebugEnabled()) {
122                log.debug("initIntensity: sent bright reset");
123            }
124        }
125    }
126
127    /**
128     * Send a Dim/Bright commands to the X10 hardware to reach a specific
129     * intensity. Acts immediately, and changes no general state.
130     * <p>
131     * This sends "Dim" commands.
132     */
133    @Override
134    protected void sendIntensity(double intensity) {
135        if (log.isDebugEnabled()) {
136            log.debug("sendIntensity({}) lastOutputStep: {} maxDimStep: {}", intensity, lastOutputStep, maxDimStep);
137        }
138
139        // if we don't know the dim count, force it to a value.
140//        if (lastOutputStep < 0) initIntensity(intensity);
141        // find the new correct dim count
142        int newStep = (int) Math.round(intensity * maxDimStep);  // maxDimStep is full on, 0 is full off, etc
143
144        // check for errors
145        if ((newStep < 0) || (newStep > maxDimStep)) {
146            log.error("newStep wrong: {} intensity: {}", newStep, intensity);
147        }
148
149        if (newStep == 0) {
150            // nothing to do!
151            if (log.isDebugEnabled()) {
152                log.debug("intensity {} within current step, return", intensity);
153            }
154            return;
155
156        }
157
158        // create output sequence of address, then function
159        X10Sequence out = new X10Sequence();
160        out.addExtData(housecode, devicecode, X10Sequence.EXTCMD_DIM, newStep);
161        // send
162        tc.sendX10Sequence(out, null);
163        lastOutputStep = newStep;
164
165        if (log.isDebugEnabled()) {
166            log.debug("sendIntensity({}) house {} device {} newStep: {}", intensity, X10Sequence.houseValueToText(housecode), devicecode, newStep);
167        }
168    }
169
170    /**
171     * Number of steps from dim to bright is maintained in specific
172     * SerialTrafficController implementation
173     */
174    @Override
175    protected int getNumberOfSteps() {
176        return tc.getNumberOfIntensitySteps();
177    }
178
179    /**
180     * Send a On/Off Command to the hardware
181     */
182    @Override
183    protected void sendOnOffCommand(int newState) {
184        if (log.isDebugEnabled()) {
185            log.debug("sendOnOff({}) Current: {}", newState, mState);
186        }
187
188        // figure out command 
189        int function;
190        double newDim;
191        if (newState == ON) {
192            function = X10Sequence.FUNCTION_ON;
193            newDim = 1;
194        } else if (newState == OFF) {
195            function = X10Sequence.FUNCTION_OFF;
196            newDim = 0;
197        } else {
198            log.warn("illegal state requested for Light: {}", getSystemName());
199            return;
200        }
201
202        log.debug("set state {} house {} device {}", newState, housecode, devicecode);
203
204        // create output sequence of address, then function
205        X10Sequence out = new X10Sequence();
206        out.addAddress(housecode, devicecode);
207        out.addFunction(housecode, function, 0);
208        // send
209        tc.sendX10Sequence(out, null);
210
211        if (log.isDebugEnabled()) {
212            log.debug("sendOnOff({})  house {} device {} funct: {}", newDim, X10Sequence.houseValueToText(housecode), devicecode, function);
213        }
214    }
215
216    private final static Logger log = LoggerFactory.getLogger(SerialX10Light.class);
217}
218
219