127 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			127 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
 | 
						|
////////////////////////////////////
 | 
						|
//   DEVICE-SPECIFIC LED SERVICES //
 | 
						|
////////////////////////////////////
 | 
						|
 | 
						|
struct DEV_GarageDoor : Service::GarageDoorOpener {     // A Garage Door Opener
 | 
						|
 | 
						|
  SpanCharacteristic *current;            // reference to the Current Door State Characteristic (specific to Garage Door Openers)
 | 
						|
  SpanCharacteristic *target;             // reference to the Target Door State Characteristic (specific to Garage Door Openers)  
 | 
						|
  SpanCharacteristic *obstruction;        // reference to the Obstruction Detected Characteristic (specific to Garage Door Openers)
 | 
						|
 | 
						|
  DEV_GarageDoor() : Service::GarageDoorOpener(){       // constructor() method
 | 
						|
        
 | 
						|
    current=new Characteristic::CurrentDoorState(1);              // initial value of 1 means closed
 | 
						|
    target=new Characteristic::TargetDoorState(1);                // initial value of 1 means closed
 | 
						|
    obstruction=new Characteristic::ObstructionDetected(false);   // initial value of false means NO obstruction is detected
 | 
						|
    
 | 
						|
    Serial.print("Configuring Garage Door Opener");   // initialization message
 | 
						|
    Serial.print("\n");
 | 
						|
 | 
						|
  } // end constructor
 | 
						|
 | 
						|
  boolean update(){                              // update() method
 | 
						|
 | 
						|
    // see HAP Documentation for details on what each value represents
 | 
						|
 | 
						|
    if(target->getNewVal()==0){                     // if the target-state value is set to 0, HomeKit is requesting the door to be in open position
 | 
						|
      LOG1("Opening Garage Door\n");
 | 
						|
      current->setVal(2);                           // set the current-state value to 2, which means "opening"
 | 
						|
      obstruction->setVal(false);                   // clear any prior obstruction detection
 | 
						|
    } else {
 | 
						|
      LOG1("Closing Garage Door\n");                // else the target-state value is set to 1, and HomeKit is requesting the door to be in the closed position
 | 
						|
      current->setVal(3);                           // set the current-state value to 3, which means "closing"         
 | 
						|
      obstruction->setVal(false);                   // clear any prior obstruction detection
 | 
						|
    }
 | 
						|
    
 | 
						|
    return(true);                               // return true
 | 
						|
  
 | 
						|
  } // update
 | 
						|
 | 
						|
  void loop(){                                     // loop() method
 | 
						|
 | 
						|
    if(current->getVal()==target->getVal())        // if current-state matches target-state there is nothing do -- exit loop()
 | 
						|
      return;
 | 
						|
 | 
						|
    if(current->getVal()==3 && random(100000)==0){    // here we simulate a random obstruction, but only if the door is closing (not opening)
 | 
						|
      current->setVal(4);                             // if our simulated obstruction is triggered, set the curent-state to 4, which means "stopped"
 | 
						|
      obstruction->setVal(true);                      // and set obstruction-detected to true
 | 
						|
      LOG1("Garage Door Obstruction Detected!\n");
 | 
						|
    }
 | 
						|
 | 
						|
    if(current->getVal()==4)                       // if the current-state is stopped, there is nothing more to do - exit loop()     
 | 
						|
      return;
 | 
						|
 | 
						|
    // This last bit of code only gets called if the door is in a state that represents actively opening or actively closing.
 | 
						|
    // If there is an obstruction, the door is "stopped" and won't start again until the HomeKit Controller requests a new open or close action
 | 
						|
 | 
						|
    if(target->timeVal()>5000)                     // simulate a garage door that takes 5 seconds to operate by monitoring time since target-state was last modified
 | 
						|
      current->setVal(target->getVal());           // set the current-state to the target-state
 | 
						|
       
 | 
						|
  } // loop
 | 
						|
  
 | 
						|
};
 | 
						|
 | 
						|
////////////////////////////////////
 | 
						|
 | 
						|
struct DEV_WindowShade : Service::WindowCovering {     // A motorized Window Shade with Hold Feature
 | 
						|
 | 
						|
  SpanCharacteristic *current;                     // reference to a "generic" Current Position Characteristic (used by a variety of different Service)
 | 
						|
  SpanCharacteristic *target;                      // reference to a "generic" Target Position Characteristic (used by a variety of different Service)
 | 
						|
 | 
						|
  DEV_WindowShade() : Service::WindowCovering(){       // constructor() method
 | 
						|
        
 | 
						|
    current=new Characteristic::CurrentPosition(0);     // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised)
 | 
						|
    
 | 
						|
    target=new Characteristic::TargetPosition(0);       // Window Shades have positions that range from 0 (fully lowered) to 100 (fully raised)
 | 
						|
    target->setRange(0,100,10);                         // set the allowable target-position range to 0-100 IN STEPS of 10
 | 
						|
        
 | 
						|
    Serial.print("Configuring Motorized Window Shade");   // initialization message
 | 
						|
    Serial.print("\n");
 | 
						|
 | 
						|
  } // end constructor
 | 
						|
 | 
						|
  boolean update(){                              // update() method
 | 
						|
 | 
						|
    // The logic below is based on how HomeKit appears to operate in practice, which is NOT consistent with
 | 
						|
    // HAP documentation.  In that document HomeKit seems to support fully opening or fully closing a window shade, with
 | 
						|
    // an optional control to HOLD the window shade at a given in-between position while it is moving.
 | 
						|
 | 
						|
    // In practice, HomeKit does not appear to implement any form of a HOLD control button, even if you instantiate that
 | 
						|
    // Characteristic.  Instead, HomeKit provides a full slider control, similar to the brightness control for a lightbulb,
 | 
						|
    // that allows you to set the exact position of the window shade from 0-100%.  This obviates the need to any sort of HOLD button.
 | 
						|
    // The resulting logic is also very simple:
 | 
						|
 | 
						|
    if(target->getNewVal()>current->getVal()){      // if the target-position requested is greater than the current-position, simply log a "raise" message  
 | 
						|
      LOG1("Raising Shade\n");                      // ** there is nothing more to do - HomeKit keeps track of the current-position so knows raising is required
 | 
						|
    } else 
 | 
						|
    if(target->getNewVal()<current->getVal()){      // if the target-position requested is less than the current-position, simply log a "raise" message  
 | 
						|
      LOG1("Lowering Shade\n");                     // ** there is nothing more to do - HomeKit keeps track of the current-position so knows lowering is required
 | 
						|
    }
 | 
						|
        
 | 
						|
    return(true);                               // return true
 | 
						|
  
 | 
						|
  } // update
 | 
						|
 | 
						|
  void loop(){                                     // loop() method
 | 
						|
 | 
						|
    // Here we simulate a window shade that takes 5 seconds to move to its new target posiiton
 | 
						|
    
 | 
						|
    if(current->getVal()!=target->getVal() && target->timeVal()>5000){          // if 5 seconds have elapsed since the target-position was last modified...
 | 
						|
      current->setVal(target->getVal());                                        // ...set the current position to equal the target position
 | 
						|
    }
 | 
						|
 | 
						|
    // Note there is no reason to send continuous updates of the current position to the HomeKit.  HomeKit does NOT display the
 | 
						|
    // current position.  Rather, it simply compares the value of the current position to the value of target positon as set by the
 | 
						|
    // the user in the Home App.  If it finds current and target positions are the same, it knows the shade is stopped.  Otherwise
 | 
						|
    // it will report the shade is raising or lowering depending on whether the specified target state is greater or less than
 | 
						|
    // the current state.
 | 
						|
 | 
						|
    // According to HAP, the Characteristic Position State is also required.  However, this seems duplicative and is NOT needed
 | 
						|
    // at all given the way HomeKit uses current position.  HomeSpan will warn you if Position State is not defined (since it 
 | 
						|
    // is technically required) but this works fine without it.
 | 
						|
    
 | 
						|
  } // loop
 | 
						|
  
 | 
						|
};
 |