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