ESP + Reorganizacja
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/*********************************************************************************
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-2024 Gregg E. Berman
|
||||
*
|
||||
* https://github.com/HomeSpan/HomeSpan
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
********************************************************************************/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// HomeSpan: A HomeKit implementation for the ESP32 //
|
||||
// ------------------------------------------------ //
|
||||
// //
|
||||
// Example 13: Target States and Current States //
|
||||
// * implementing a Garage Door Opener //
|
||||
// * implementing a motorized Window Shade //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "HomeSpan.h"
|
||||
#include "DEV_DoorsWindows.h"
|
||||
|
||||
void setup() {
|
||||
|
||||
// In Example 12 we saw how to implement the loop() method for a Service to continuously monitor our device and periodically report
|
||||
// changes in one or more Characteristics back to HomeKit using setVal() and timeVal(). In that example we implemented passive sensors
|
||||
// that operated independently and required no input from the user, which meant we did not need to implement any update() methods.
|
||||
|
||||
// In this Example 13 we demonstrate the simultaneous use of both the update() and loop() methods by implementing two new Services:
|
||||
// a Garage Door Opener and a motorized Window Shade. Both examples showcase HomeKit's Target-State/Current-State framework.
|
||||
// For physical devices that take time to operate (such as closing a door), HomeKit Services typically use:
|
||||
|
||||
// * one Characteristic that HomeKit sets via update() requests to HomeSpan, and that represent a desired target state,
|
||||
// such as opened, closed, or in some cases a percentage opened or closed; and
|
||||
|
||||
// * one read-only Characteristic that HomeSpan use to track the current state of the device in the loop() method, as well as
|
||||
// report back changes to HomeKit using setVal().
|
||||
|
||||
// Not all HomeKit Services utilize the same Characteristics to define target and current states. Some Services use Characteristics
|
||||
// that are specific to that one Service, whereas others use more generic Characteristics. The common theme seems to be that HomeKit
|
||||
// guesses the actions a device is taking, and updates it tile's icon accordingly, by comparing the value of the target state
|
||||
// Characteristic it sets, and the current state Characteristic it receives in the form of Event Notifications. When they are the same,
|
||||
// HomeKit assumes the physical device has reached the required position. When they differ, HomeKit assumes something will be opening,
|
||||
// closing, raising, lowering, etc. Sometimes a little experimenting and a lot of trial and error is required to fully understand how
|
||||
// each Service responds to different combinations of Characteristic values.
|
||||
|
||||
// As always, we won't be connecting our ESP32 to an actual garage door or window shade but will instead simulate their responses and
|
||||
// actions for illustrative purposes. In some ways the code is more complicated because of the need to simulate values - it might be
|
||||
// easier if we actually were connecting to a garage door or window shade!
|
||||
|
||||
// Fully commented code for both of our derived Services can be found in DEV_DoorsWindows.h. These examples do not introduce any new
|
||||
// HomeSpan functions, but you will see how to use HomeSpan's ENUMERATED CONSTANTS, instead of just plain integers, to set the values
|
||||
// of Characteristics where the values represent discrete states (e.g. "lowering", "opening").
|
||||
|
||||
// Please see HomeSpan's Services and Characteristics page for a complete list of the enumerated constants available for Characteristics
|
||||
// where they are applicable.
|
||||
|
||||
Serial.begin(115200);
|
||||
|
||||
homeSpan.begin(Category::Bridges,"HomeSpan Bridge");
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Garage Door");
|
||||
new DEV_GarageDoor(); // Create a Garage Door Opener (see DEV_DoorsWindows.h for definition)
|
||||
|
||||
new SpanAccessory();
|
||||
new Service::AccessoryInformation();
|
||||
new Characteristic::Identify();
|
||||
new Characteristic::Name("Window Shade");
|
||||
new DEV_WindowShade(); // Create a motorized Window Shade (see DEV_DoorsWindows.h for definition)
|
||||
|
||||
} // end of setup()
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
void loop(){
|
||||
|
||||
homeSpan.poll();
|
||||
|
||||
} // end of loop()
|
||||
@@ -0,0 +1,129 @@
|
||||
|
||||
////////////////////////////////////
|
||||
// DEVICE-SPECIFIC LED SERVICES //
|
||||
////////////////////////////////////
|
||||
|
||||
struct DEV_GarageDoor : Service::GarageDoorOpener { // A Garage Door Opener
|
||||
|
||||
Characteristic::CurrentDoorState *current; // reference to the Current Door State Characteristic (specific to Garage Door Openers)
|
||||
Characteristic::TargetDoorState *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
|
||||
|
||||
// Below we use enumerated constants rather than integers to set the values of the Characteristics.
|
||||
// Using enumerated constants means not having to remember the integer code for each state. You'll find
|
||||
// a complete list of all available enumerated constants on HomeSpan's Services and Characteristics page.
|
||||
// Note the use of enumerated constants is optional - you can always use the integer code representing
|
||||
// each state instead.
|
||||
|
||||
current=new Characteristic::CurrentDoorState(Characteristic::CurrentDoorState::CLOSED); // here we use the fully-qualified name of the constant "CLOSED"
|
||||
target=new Characteristic::TargetDoorState(target->CLOSED); // here we use the name of the object instead of the fully-qualified name (much less typing)
|
||||
|
||||
// Below we must use the fully-qualified name of the enumerated constant and cannot use "obstruction->NOT_DETECTED".
|
||||
// Why? Because above we declared "obstruction" to be a pointer to a generic SpanCharacteristic instead of a pointer to
|
||||
// the more specific Characteristic::ObstructionDetected. Either is fine, and it's just a matter of programming preference
|
||||
// (as you can see we use both conventions in this sketch). But the downside of using SpanCharacteristic to declare a
|
||||
// Characteristic that contains enumerated constants is that the object itself does not know about these constants. This is
|
||||
// because all enumerated constants are uniquely defined within their respective specific Characteristic classes, and not in the
|
||||
// generic SpanCharacteristic class from which all specific Characterstics are derived.
|
||||
|
||||
obstruction=new Characteristic::ObstructionDetected(Characteristic::ObstructionDetected::NOT_DETECTED); // this works
|
||||
// obstruction=new Characteristic::ObstructionDetected(obstruction->NOT_DETECTED); // this would produce a compiler error (try it and see)
|
||||
|
||||
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()==target->OPEN){ // HomeKit is requesting the door to be in OPEN position
|
||||
LOG1("Opening Garage Door\n");
|
||||
current->setVal(current->OPENING); // set the current-state value to OPENING
|
||||
obstruction->setVal(false); // clear any prior obstruction detection - note we do not bother using an enumerated constant here
|
||||
} else {
|
||||
LOG1("Closing Garage Door\n"); // else HomeKit must be requesting the door to be in the CLOSED position
|
||||
current->setVal(current->CLOSING); // set the current-state value to 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()==current->CLOSING && random(100000)==0){ // here we simulate a random obstruction, but only if the door is closing (not opening)
|
||||
current->setVal(current->STOPPED); // if our simulated obstruction is triggered, set the curent-state to STOPPED
|
||||
obstruction->setVal(true); // and set obstruction-detected to true
|
||||
LOG1("Garage Door Obstruction Detected!\n");
|
||||
}
|
||||
|
||||
if(current->getVal()==current->STOPPED) // 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
|
||||
|
||||
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.
|
||||
|
||||
} // loop
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user