├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── bins ├── global-kauf-plf10.bin ├── global-kauf-plf12.bin └── global-sonoff-s31.bin ├── esphome ├── .gitignore ├── header-files │ ├── hebrewcalender_melachaplug.h │ └── melachaplug_main.h ├── melachaplug-8285.yaml ├── melachaplug-cloudfree-p2.yaml ├── melachaplug-esp32.yaml ├── melachaplug-plf10.yaml ├── melachaplug-s31.yaml └── plugins │ ├── melachaplug │ ├── location_info.yaml │ ├── melacha_config.yaml │ ├── optional_config.yaml │ └── plug_info.yaml │ ├── next_shabbbos_times │ ├── next_shabbbos_times.h │ └── next_shabbbos_times.yaml │ └── random_minute_generator │ ├── random_minute_generator.h │ └── random_minute_generator.yaml └── images ├── Melacha Plug Banner.png └── Web Server Screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore settings for ESPHome 2 | # This is an example and may include too much for your use-case. 3 | # You can modify this file to suit your needs. 4 | /.vscode/ 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "xstring": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Melacha Plug License 2 | 3 | 4 | 5 | 6 | GNU Lesser General Public License 7 | Version 2.1, February 1999 8 | 9 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. 10 | 11 | [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. 16 | 17 | This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. 18 | 19 | When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. 20 | 21 | To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. 22 | 23 | For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. 24 | 25 | We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. 26 | 27 | To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. 28 | 29 | Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. 30 | 31 | Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. 32 | 33 | When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. 34 | 35 | We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. 36 | 37 | For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. 38 | 39 | In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. 40 | 41 | Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. 42 | 43 | The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. 44 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 45 | 46 | 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". 47 | 48 | A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. 49 | 50 | The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) 51 | 52 | "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. 53 | 54 | Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 55 | 56 | 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. 57 | 58 | You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 59 | 60 | 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: 61 | 62 | a) The modified work must itself be a software library. 63 | 64 | b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. 65 | 66 | c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. 67 | 68 | d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. 69 | 70 | (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) 71 | 72 | These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. 73 | 74 | Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. 75 | 76 | In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 77 | 78 | 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. 79 | 80 | Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. 81 | 82 | This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 83 | 84 | 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. 85 | 86 | If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 87 | 88 | 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. 89 | 90 | However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. 91 | 92 | When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. 93 | 94 | If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) 95 | 96 | Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 97 | 98 | 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. 99 | 100 | You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: 101 | 102 | a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) 103 | 104 | b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. 105 | 106 | c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. 107 | 108 | d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. 109 | 110 | e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. 111 | 112 | For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. 113 | 114 | It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 115 | 116 | 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: 117 | 118 | a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. 119 | 120 | b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 121 | 122 | 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 123 | 124 | 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 125 | 126 | 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 127 | 128 | 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. 129 | 130 | If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. 131 | 132 | It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. 133 | 134 | This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 135 | 136 | 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 137 | 138 | 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. 139 | 140 | Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 141 | 142 | 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. 143 | 144 | NO WARRANTY 145 | 146 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 147 | 148 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Melacha Plug Banner](https://github.com/chabad-source/melachaplug/blob/main/images/Melacha%20Plug%20Banner.png)](https://github.com/chabad-source/melachaplug) 2 | 3 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 4 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/f17caa6e3d2946378de9beae9fc0ffe8)](https://www.codacy.com/gh/chabad-source/melachaplug/dashboard?utm_source=github.com&utm_medium=referral&utm_content=chabad-source/melachaplug&utm_campaign=Badge_Grade) 5 | [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?hosted_button_id=Q9A7HG8NQEJRU) 6 | 7 | # Melacha Plug 8 | A [Melacha](https://www.chabad.org/95906/) aware plug which knows when it's [Shabbos](https://www.chabad.org/633659/) or [Yom Tov](https://www.chabad.org/holidays/default_cdo/jewish/holidays.htm). 9 | 10 | This project is using [ESPHome](https://esphome.io/). 11 | 12 | Looking to disable a smart doorbell for Shabbos? 13 | Worried about Google or Alexa accidentally triggering on Shabbos or Yom Tov? 14 | The solution is here! 15 | 16 | ## Features 17 | - [x] Smart: It features a full Jewish calendar on board, not just for Shabbos, but also for Yom Tov! 18 | - [x] Failsafe: Buttons are disabled on Shabbos and Yom Tov for added security. 19 | - [x] Intelligent: It automatically detects your location and provides the option to further customize. 20 | - [x] Aware: A LED flash lets you know when Shabbos mode is enabled. 21 | - [x] Local: All logic is processed on board. 22 | - [x] Adaptable: All code is open source, allowing for easy tweaking, correction, and expansion. 23 | - [x] Easy: Simply plug and play, and you're all set! 24 | 25 | ![Web Server Screenshot](https://github.com/chabad-source/melachaplug/blob/main/images/Web%20Server%20Screenshot.png) 26 | 27 | ## Getting Started 28 | 29 | ### What You'll Need 30 | - A plug with ESPHome or Tasmota software installed. 31 | - Pre-flashed plugs can be bought from the [Athom](https://www.athom.tech/) or [Cloud Free](https://cloudfree.shop/) shop. There is also some preflashed plugs to be found on [Ebay](https://www.ebay.com/sch/i.html?_nkw=preflashed+smart+plug), and [Amazon](https://www.amazon.com/s?k=KAUF+Esphome). 32 | 33 | ## Installation 34 | 35 | ### Pre-configured 36 | 37 | *Limitations* 38 | - Timezone is locked to EST unless you flash it yourself. 39 | - Your location won't be 100% accurate (you can update it via the web UI). 40 | 41 | *Instructions* 42 | - Download the melachaplug.bin file from above. (currently none are compiled) 43 | - If your device is using Tasmota, follow instructions on how to upgrade from the [ESPHome docs](https://esphome.io/guides/migrate_sonoff_tasmota.html) or [Tasmota docs](https://tasmota.github.io/docs/Upgrading/#upgrade-using-webui). 44 | - If your device is using ESPHome, use the web UI to install the firmware. 45 | - Once the firmware is installed it will create a WiFi access point called "Melacha Plug Fallback", connect to it using any phone or computer. 46 | - On the devices webpage enter in your WiFi details (it should pop up, if it doesn't then it can be accessed via it's IP). 47 | - The device will restart, and should appear on your WiFi network. 48 | 49 | ### Custom Configuration 50 | - Set up your own ESPhome instance. If you have [Home Assistant](https://www.home-assistant.io/) then use the [ESPHome Dashboard](https://esphome.io/guides/getting_started_hassio.html), otherwise follow the [Command Line Interface](https://esphome.io/guides/getting_started_command_line.html) guide. 51 | - Find the YAML file for your device from the esphome folder above. 52 | - Configure the YAML settings to your liking, and flash device (more details on the ESPHome site). 53 | 54 | ## Advanced 55 | 56 | ### Using Relays 57 | You can customize this project for any sort of relays, including the sonoff basic. 58 | 59 | ### Shabbos Mode 60 | Shabbos from this project can be reused to be helpful in all types of projects. 61 | 62 | ### Potential Use Cases 63 | - Those with motion based lights, can have the motion disabled on Shabbos and Yom Tov. 64 | - Preserve energy usage during Shabbos and Yom Tov, for example of water heaters (temperature should be kept above °120F to prevent [Legionnaires’ disease](https://www.cdc.gov/legionella/wmp/control-toolkit/potable-water-systems.html)). 65 | - Turn off security cameras. 66 | - Disable accidental light switch triggers on Shabbos and Yom Tov (advanced). 67 | - Turn on a hot water urn before Shabbos and have it auto turn off after. 68 | - Disable a dishwasher so it won't accidentally get turned on. 69 | 70 | 71 | ## Contribute 72 | 73 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/donate/?hosted_button_id=Q9A7HG8NQEJRU) - or - [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/rebbepod) 74 | -------------------------------------------------------------------------------- /bins/global-kauf-plf10.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chabad-source/melachaplug/9a3a00ae4f65cfa1f3cb6a996ffc9bd2418f0be0/bins/global-kauf-plf10.bin -------------------------------------------------------------------------------- /bins/global-kauf-plf12.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chabad-source/melachaplug/9a3a00ae4f65cfa1f3cb6a996ffc9bd2418f0be0/bins/global-kauf-plf12.bin -------------------------------------------------------------------------------- /bins/global-sonoff-s31.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chabad-source/melachaplug/9a3a00ae4f65cfa1f3cb6a996ffc9bd2418f0be0/bins/global-sonoff-s31.bin -------------------------------------------------------------------------------- /esphome/.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore settings for ESPHome 2 | # This is an example and may include too much for your use-case. 3 | # You can modify this file to suit your needs. 4 | /.esphome/ 5 | /secrets.yaml 6 | -------------------------------------------------------------------------------- /esphome/header-files/hebrewcalender_melachaplug.h: -------------------------------------------------------------------------------- 1 | /**** 2 | Adapted from https://github.com/yparitcher/libzmanim 3 | 4 | --------------------------------------------------------------------------------- 5 | Copyright (c) 2018 Y Paritcher 6 | This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser GeneralPublic License as published by the Free Software Foundation; 7 | either version 2.1 of the License, or (at your option)any later version. 8 | This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; 9 | without even the impliedwarranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | See the GNU Lesser General Public License for more details. 11 | You should have received a copy of the GNU Lesser General Public License along with this library; 12 | if not, write tothe Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA, 13 | or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 14 | 15 | --------------------------------------------------------------------------------- 16 | Based on code from Calendrical Calculations 17 | by Nachum Dershowitz and Edward M. Reingold, 18 | Software---Practice & Experience, vol. 20, no. 9 (September, 1990), pp. 899--928. 19 | and code from Astronomical Algorithms by Jean Meeus 20 | and code from ICU licensed under the Unicode license 21 | COPYRIGHT AND PERMISSION NOTICE (ICU 58 and later) 22 | Copyright © 1991-2018 Unicode, Inc. All rights reserved. 23 | Distributed under the Terms of Use in http://www.unicode.org/copyright.html. 24 | Permission is hereby granted, free of charge, to any person obtaining 25 | a copy of the Unicode data files and any associated documentation 26 | (the "Data Files") or Unicode software and any associated documentation 27 | (the "Software") to deal in the Data Files or Software 28 | without restriction, including without limitation the rights to use, 29 | copy, modify, merge, publish, distribute, and/or sell copies of 30 | the Data Files or Software, and to permit persons to whom the Data Files 31 | or Software are furnished to do so, provided that either 32 | (a) this copyright and permission notice appear with all copies 33 | of the Data Files or Software, or 34 | (b) this copyright and permission notice appear in associated 35 | Documentation. 36 | THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF 37 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 38 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 39 | NONINFRINGEMENT OF THIRD PARTY RIGHTS. 40 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS 41 | NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL 42 | DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 43 | DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 44 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 45 | PERFORMANCE OF THE DATA FILES OR SOFTWARE. 46 | Except as contained in this notice, the name of a copyright holder 47 | shall not be used in advertising or otherwise to promote the sale, 48 | use or other dealings in these Data Files or Software without prior 49 | written authorization of the copyright holder. 50 | --------------------------------------------------------------------------------- 51 | ****/ 52 | 53 | 54 | 55 | 56 | 57 | //------------------------------------------------- hebrewcalendar.h ---------------------------------------------------------- 58 | 59 | #pragma once 60 | 61 | #include 62 | 63 | // location struct used to calculate zmanim 64 | typedef struct { 65 | double latitude; 66 | double longitude; 67 | double elevation; 68 | } location; 69 | 70 | // enum of parshahs 71 | typedef enum {NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT, MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM, VAYELECH, HAAZINU, VZOT_HABERACHAH, VAYAKHEL_PEKUDEI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, BEHAR_BECHUKOTAI, CHUKAT_BALAK, MATOT_MASEI, NITZAVIM_VAYELECH} parshah; 72 | 73 | // enum of yomtovs 74 | typedef enum {CHOL, PESACH_DAY1, PESACH_DAY2, SHVEI_SHEL_PESACH, ACHRON_SHEL_PESACH, SHAVOUS_DAY1, SHAVOUS_DAY2, ROSH_HASHANAH_DAY1, ROSH_HASHANAH_DAY2, YOM_KIPPUR, SUKKOS_DAY1, SUKKOS_DAY2, SHMEINI_ATZERES, SIMCHAS_TORAH, CHOL_HAMOED_PESACH_DAY1, CHOL_HAMOED_PESACH_DAY2, CHOL_HAMOED_PESACH_DAY3, CHOL_HAMOED_PESACH_DAY4, CHOL_HAMOED_PESACH_DAY5, CHOL_HAMOED_SUKKOS_DAY1, CHOL_HAMOED_SUKKOS_DAY2, CHOL_HAMOED_SUKKOS_DAY3, CHOL_HAMOED_SUKKOS_DAY4, CHOL_HAMOED_SUKKOS_DAY5, HOSHANA_RABBAH, PESACH_SHEINI, LAG_BAOMER, TU_BAV, CHANUKAH_DAY1, CHANUKAH_DAY2, CHANUKAH_DAY3, CHANUKAH_DAY4, CHANUKAH_DAY5, CHANUKAH_DAY6, CHANUKAH_DAY7, CHANUKAH_DAY8, TU_BISHVAT, PURIM_KATAN, SHUSHAN_PURIM_KATAN, PURIM, SHUSHAN_PURIM, SHIVA_ASAR_BTAAMUZ, TISHA_BAV, TZOM_GEDALIA, ASARAH_BTEVES, TAANIS_ESTER, EREV_PESACH, EREV_SHAVOUS, EREV_ROSH_HASHANAH, EREV_YOM_KIPPUR, EREV_SUKKOS, SHKALIM, ZACHOR, PARAH, HACHODESH, ROSH_CHODESH, MACHAR_CHODESH, SHABBOS_MEVORCHIM} yomtov; 75 | 76 | //array of parshah year types 77 | extern const parshah parshahlist[17][56]; 78 | 79 | // hdate struct 80 | typedef struct { 81 | int year; // 0 - ∞ 82 | int month; // starts from nissan 1 - 13 83 | int day; // 1 - 30 84 | int hour; // 0 - 23 85 | int min; // 0 - 59 86 | int sec; // 0 - 59 87 | int msec; 88 | int wday; // weekdays 1 - 6, 0 89 | int dayofyear; // starts from tishrei 90 | long int offset; // timezone offset in seconds 91 | _Bool leap; // if leap year 92 | _Bool EY; // if Eretz Yisroel (for yomtov & parshah) 93 | } hdate; 94 | 95 | // molad type: beware that the seconds are actually chalakim 1/1080 of an hour 96 | // and that the hebrew date is actually the next one when the hour > 18 97 | // also the molad is calculated in Yerushalayim Mean Time so cannot be easily converted. 98 | typedef hdate molad; 99 | 100 | // get length of given month 101 | int LastDayOfHebrewMonth(int month, int year); 102 | 103 | // is the year a leap year 104 | int HebrewLeapYear(int year); 105 | 106 | // day from molad tohu until Rosh Hashana 107 | long int HebrewCalendarElapsedDays(int year); 108 | // convert struct tm to hdate 109 | hdate convertDate(struct tm date); 110 | void setEY(hdate *date, _Bool EY); 111 | // convert a hdate to julian day 112 | double hdatejulian(hdate date); 113 | // convert a hdate to gregorian date 114 | struct tm hdategregorian(hdate date); 115 | // convert a gregorian date to julian day 116 | double gregorianjulian(struct tm date); 117 | // convert a hdate to a time_t 118 | time_t hdatetime_t(hdate date); 119 | 120 | //compare 2 hdate: 121 | // returns 0 if they are the same 122 | // 1 if date1 < date2 123 | // and -1 if date1 > date2 124 | int hdatecompare(hdate date1, hdate date2); 125 | 126 | // functions to add or subtract from a hdate field and then normalize the result 127 | void hdateaddyear(hdate *date, int years); 128 | void hdateaddmonth(hdate *date, int months); 129 | void hdateaddday(hdate *date, int days); 130 | void hdateaddhour(hdate *date, int hours); 131 | void hdateaddminute(hdate *date, int minutes); 132 | void hdateaddsecond(hdate *date, int seconds); 133 | void hdateaddmsecond(hdate *date, int mseconds); 134 | void hdateadd(hdate *date, int years, int months, int days, int hours, int minutes, int seconds, int mseconds); 135 | 136 | 137 | // Returns the molad of the given month remember month 1 is Nissan 138 | // beware that the seconds are actually chalakim 1/1080 of an hour 139 | // and that the hebrew date is actually the next one when the hour > 18 140 | // also the molad is calculated in Yerushalayim Mean Time so cannot be easily converted. 141 | molad getmolad(int year, int month); 142 | 143 | // if Shabbos get the current parshah otherwise return 0 144 | parshah getparshah(hdate date); 145 | // if yomtov get the current yomtov otherwise return 0 146 | yomtov getyomtov(hdate date); 147 | // if Shabbos get the current special parshah otherwise return 0 148 | yomtov getspecialshabbos(hdate date); 149 | // if rosh chodesh return rosh chodesh otherwise return 0 150 | yomtov getroshchodesh(hdate date); 151 | // if machar chodesh return machar chodesh otherwise return 0 152 | yomtov getmacharchodesh(hdate date); 153 | // if shabbos mevorchim return shabbos mevorchim otherwise return 0 154 | yomtov getshabbosmevorchim(hdate date); 155 | // The omer count 1 - 49 or 0 if none 156 | int getomer(hdate date); 157 | // if Shabbos get the current chapter of avos otherwise return 0 158 | // returns 1 - 6 or 12 or 34 or 56 for double chapter 159 | int getavos(hdate date); 160 | 161 | _Bool istaanis(hdate date); 162 | _Bool isassurbemelachah(hdate date); 163 | // return 1 if cadlelighting regular, 2 if at nightfall, 3 if chanukah, or 0 if none 164 | int iscandlelighting(hdate date); 165 | 166 | // return true if birchas hachama on date 167 | _Bool isbirchashachama(hdate date); 168 | 169 | // return true if birchas hashanim is switched on date (winter) 170 | _Bool isbirchashashanim(hdate date); 171 | // return true if tal umatar livrachah is said in birchas hashanim 172 | _Bool getbirchashashanim(hdate date); 173 | 174 | 175 | 176 | 177 | //------------------------------------------------- hebrewcalendar.c ---------------------------------------------------------- 178 | 179 | #include 180 | 181 | const parshah parshahlist[17][56] = { 182 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 183 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NOPARSHAH, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT_BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 184 | {NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 185 | {NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 186 | {NOPARSHAH, NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 187 | {NOPARSHAH, NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 188 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NOPARSHAH, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT_BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 189 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 190 | {NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, ACHAREI_MOT, NOPARSHAH, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT, MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 191 | {NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, ACHAREI_MOT, NOPARSHAH, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT, MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 192 | {NOPARSHAH, NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 193 | {NOPARSHAH, NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NOPARSHAH, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT_BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 194 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR_BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 195 | {NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL_PEKUDEI, VAYIKRA, TZAV, NOPARSHAH, SHEMINI, TAZRIA_METZORA, ACHAREI_MOT_KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 196 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}, 197 | {NOPARSHAH, VAYELECH, HAAZINU, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT, MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM}, 198 | {NOPARSHAH, NOPARSHAH, HAAZINU, NOPARSHAH, NOPARSHAH, BERESHIT, NOACH, LECH_LECHA, VAYEIRA, CHAYEI_SARAH, TOLEDOT, VAYETZE, VAYISHLACH, VAYESHEV, MIKETZ, VAYIGASH, VAYECHI, SHEMOT, VAEIRA, BO, BESHALACH, YITRO, MISHPATIM, TERUMAH, TETZAVEH, KI_TISA, VAYAKHEL, PEKUDEI, VAYIKRA, TZAV, SHEMINI, TAZRIA, METZORA, NOPARSHAH, ACHAREI_MOT, KEDOSHIM, EMOR, BEHAR, BECHUKOTAI, BAMIDBAR, NASO, BEHAALOTECHA, SHLACH, KORACH, CHUKAT, BALAK, PINCHAS, MATOT_MASEI, DEVARIM, VAETCHANAN, EIKEV, REEH, SHOFTIM, KI_TEITZEI, KI_TAVO, NITZAVIM_VAYELECH}}; 199 | 200 | int HebrewLeapYear(int year) 201 | { 202 | if ((((7 * year) + 1) % 19) < 7) 203 | return 1; 204 | else 205 | return 0; 206 | } 207 | 208 | long int HebrewCalendarElapsedDays(int year) 209 | { 210 | long int MonthsElapsed = 211 | (235 * ((year - 1) / 19)) // Months in complete cycles so far. 212 | + (12 * ((year - 1) % 19)) // Regular months in this cycle. 213 | + (7 * ((year - 1) % 19) + 1) / 19; // Leap months this cycle 214 | long int PartsElapsed = 204 + 793 * (MonthsElapsed % 1080); 215 | long int HoursElapsed = 216 | 5 + 12 * MonthsElapsed + 793 * (MonthsElapsed / 1080) 217 | + PartsElapsed / 1080; 218 | long int ConjunctionDay = 1 + 29 * MonthsElapsed + HoursElapsed / 24; 219 | long int ConjunctionParts = 1080 * (HoursElapsed % 24) + PartsElapsed % 1080; 220 | long int AlternativeDay; 221 | int cdw = (ConjunctionDay % 7); 222 | if ((ConjunctionParts >= 19440) // If new moon is at or after midday, 223 | || ((cdw == 2) // ...or is on a Tuesday... 224 | && (ConjunctionParts >= 9924) // at 9 hours, 204 parts or later... 225 | && !(HebrewLeapYear(year))) // ...of a common year, 226 | || ((cdw == 1) // ...or is on a Monday at... 227 | && (ConjunctionParts >= 16789) // 15 hours, 589 parts or later... 228 | && (HebrewLeapYear(year - 1))))// at the end of a leap year 229 | // Then postpone Rosh HaShanah one day 230 | AlternativeDay = ConjunctionDay + 1; 231 | else 232 | AlternativeDay = ConjunctionDay; 233 | int adw = (AlternativeDay % 7); 234 | if (( adw == 0)// If Rosh HaShanah would occur on Sunday, 235 | || (adw == 3) // or Wednesday, 236 | || (adw == 5)) // or Friday 237 | // Then postpone it one (more) day 238 | return (1+ AlternativeDay); 239 | else 240 | return AlternativeDay; 241 | } 242 | 243 | int DaysInHebrewYear(int year) 244 | { 245 | return ((HebrewCalendarElapsedDays(year + 1)) - 246 | (HebrewCalendarElapsedDays(year))); 247 | } 248 | 249 | int LongHeshvan(int year) 250 | { 251 | if ((DaysInHebrewYear(year) % 10) == 5) 252 | return 1; 253 | else 254 | return 0; 255 | } 256 | 257 | int ShortKislev(int year) 258 | { 259 | if ((DaysInHebrewYear(year) % 10) == 3) 260 | return 1; 261 | else 262 | return 0; 263 | } 264 | 265 | int LastDayOfHebrewMonth(int month, int year) 266 | { 267 | if ((month == 2) 268 | || (month == 4) 269 | || (month == 6) 270 | || ((month == 8) && !(LongHeshvan(year))) 271 | || ((month == 9) && ShortKislev(year)) 272 | || (month == 10) 273 | || ((month == 12) && !(HebrewLeapYear(year))) 274 | || (month == 13)) 275 | return 29; 276 | else 277 | return 30; 278 | } 279 | 280 | int nissanCount(int year) 281 | { 282 | int count= 0; 283 | switch(DaysInHebrewYear(year)) 284 | { 285 | case 353: 286 | count = 176; 287 | break; 288 | case 354: 289 | count = 177; 290 | break; 291 | case 355: 292 | count = 178; 293 | break; 294 | case 383: 295 | count = 206; 296 | break; 297 | case 384: 298 | count = 207; 299 | break; 300 | case 385: 301 | count = 208; 302 | break; 303 | } 304 | return count; 305 | } 306 | 307 | hdate convertDate(struct tm date) 308 | { 309 | hdate result; 310 | double julianDay = gregorianjulian(date); 311 | long int d = (long int)julianDay - 347996; 312 | double m = ((d * (double)25920) / (double)765433); 313 | int year = (int)((19. * m) / 235.); 314 | int month; 315 | int dayCount; 316 | 317 | while (d >= HebrewCalendarElapsedDays(year + 1)) 318 | { 319 | year++; 320 | } 321 | long int ys = HebrewCalendarElapsedDays(year); 322 | int dayOfYear = (d - ys)+1; 323 | int nissanStart = nissanCount(year); 324 | if (dayOfYear <= nissanStart) { 325 | month = 7; // Start at Tishri 326 | dayCount = 0; 327 | } else { 328 | month = 1; // Start at Nisan 329 | dayCount = nissanStart; 330 | } 331 | while (dayOfYear > (dayCount + LastDayOfHebrewMonth(month, year))) 332 | { 333 | dayCount += LastDayOfHebrewMonth(month, year); 334 | month++; 335 | } 336 | int day = dayOfYear - dayCount ; 337 | result.year = year; 338 | result.month = month; 339 | result.day = day; 340 | result.wday = (HebrewCalendarElapsedDays(year)+dayOfYear)%7; 341 | result.dayofyear = dayOfYear; 342 | result.leap = HebrewLeapYear(year); 343 | result.hour = date.tm_hour; 344 | result.min = date.tm_min; 345 | result.sec = date.tm_sec; 346 | return result; 347 | } 348 | 349 | void setEY(hdate *date, _Bool EY) 350 | { 351 | date->EY = EY; 352 | } 353 | 354 | double hdatejulian(hdate date) 355 | { 356 | double diff = 347996.5; 357 | long int yearstart = HebrewCalendarElapsedDays(date.year); 358 | return (date.dayofyear-1) + yearstart + diff; 359 | } 360 | 361 | struct tm hdategregorian(hdate date) 362 | { 363 | struct tm result; 364 | double jd = hdatejulian(date)+.5; 365 | double A = floor((jd - 1867216.25)/36524.25); 366 | double B = (jd + 1525 + A - floor(A/4)); 367 | double C = floor((B-122.1)/365.25); 368 | double D = floor(C*365.25); 369 | double E = floor((B-D)/30.6001); 370 | int m,y; 371 | if (E>13){m = E-13;}else{m = E-1;} 372 | if (m>2){y = C-4716;}else{y = C-4715;} 373 | result.tm_year = y-1900; 374 | result.tm_mon = m-1; 375 | result.tm_mday = (B - D - floor(E*30.6001)); 376 | result.tm_hour = date.hour; 377 | result.tm_min = date.min; 378 | result.tm_sec = date.sec; 379 | result.tm_isdst=-1; 380 | mktime(&result); 381 | return result; 382 | } 383 | 384 | time_t hdatetime_t(hdate date) 385 | { 386 | time_t result = (HebrewCalendarElapsedDays(date.year)+(date.dayofyear-1))-2092591; 387 | result = ((((((result*24)+date.hour)*60)+date.min)*60)+date.sec); 388 | result -= date.offset; 389 | return result; 390 | } 391 | 392 | double gregorianjulian(struct tm date) 393 | { 394 | int year = date.tm_year + 1900; 395 | int month = date.tm_mon + 1; 396 | int day = date.tm_mday; 397 | if (month <= 2) { 398 | year -= 1; 399 | month += 12; 400 | } 401 | int A = floor(year/100); 402 | int B = 2 - A + floor(A/4); 403 | 404 | double JD = floor(365.25*(year + 4716)) + floor(30.6001*(month+1)) + day + B - 1524.5; 405 | return JD; 406 | } 407 | 408 | int hdatecompare(hdate date1, hdate date2) 409 | { 410 | if (date1.year < date2.year){return 1;} 411 | else if (date1.year > date2.year){return -1;} 412 | else if (date1.dayofyear < date2.dayofyear){return 1;} 413 | else if (date1.dayofyear > date2.dayofyear){return -1;} 414 | else if (date1.hour < date2.hour){return 1;} 415 | else if (date1.hour > date2.hour){return -1;} 416 | else if (date1.min < date2.min){return 1;} 417 | else if (date1.min > date2.min){return -1;} 418 | else if (date1.sec < date2.sec){return 1;} 419 | else if (date1.sec > date2.sec){return -1;} 420 | else if (date1.msec < date2.msec){return 1;} 421 | else if (date1.msec > date2.msec){return -1;} 422 | else {return 0;} 423 | } 424 | 425 | void hdatesetdoy(hdate *date) 426 | { 427 | int year = date->year; 428 | int month = date->month; 429 | int day = date->day; 430 | if (day == 30 && LastDayOfHebrewMonth(month, year) == 29){day = 29;} 431 | int monthcount; 432 | int dayOfYear; 433 | if (month < 7) 434 | { 435 | monthcount = 1; 436 | dayOfYear = nissanCount(year); 437 | } else { 438 | monthcount = 7; 439 | dayOfYear = 0; 440 | } 441 | while (monthcount < month) 442 | { 443 | dayOfYear += LastDayOfHebrewMonth(monthcount, year); 444 | monthcount++; 445 | } 446 | dayOfYear += day; 447 | date->dayofyear = dayOfYear; 448 | date->wday = (HebrewCalendarElapsedDays(year)+dayOfYear)%7; 449 | date->leap = HebrewLeapYear(year); 450 | } 451 | 452 | void hdateaddyear(hdate *date, int years) 453 | { 454 | int year = date->year; 455 | int month = date->month; 456 | int leap1 = date->leap; 457 | year += years; 458 | int leap2 = HebrewLeapYear(year); 459 | if (leap1 != leap2) 460 | { 461 | if (leap1 && month == 13){month = 12;} 462 | if (!leap1 && month == 12){month = 13;} 463 | } 464 | date->year = year; 465 | date->month = month; 466 | hdatesetdoy(date); 467 | } 468 | 469 | void hdateaddmonth(hdate *date, int months) 470 | { 471 | _Bool last = 0; 472 | if (date->day == 30) {last = 1;} 473 | int monthcount = months; 474 | while (monthcount > 0) 475 | { 476 | switch (date->month) 477 | { 478 | case 12: 479 | if (date->leap){date->month++;} 480 | else{date->month = 1;} 481 | monthcount--; 482 | break; 483 | case 13: 484 | date->month = 1; 485 | monthcount--; 486 | break; 487 | case 6: 488 | date->month++; 489 | hdateaddyear(date, 1); 490 | monthcount--; 491 | break; 492 | default: 493 | date->month++; 494 | monthcount--; 495 | break; 496 | } 497 | } 498 | while (monthcount < 0) 499 | { 500 | switch (date->month) 501 | { 502 | case 1: 503 | if (date->leap){date->month = 13;} 504 | else{date->month = 12;} 505 | monthcount++; 506 | break; 507 | case 7: 508 | date->month--; 509 | hdateaddyear(date, -1); 510 | monthcount++; 511 | break; 512 | default: 513 | date->month--; 514 | monthcount++; 515 | break; 516 | } 517 | } 518 | if (last && LastDayOfHebrewMonth(date->month, date->year) == 30){date->day = 30;} 519 | hdatesetdoy(date); 520 | } 521 | 522 | void hdateaddday(hdate *date, int days) 523 | { 524 | int daycount = days; 525 | while (daycount > 0) 526 | { 527 | switch (date->day) 528 | { 529 | case 30: 530 | date->day = 1; 531 | hdateaddmonth(date, 1); 532 | daycount--; 533 | break; 534 | case 29: 535 | if (LastDayOfHebrewMonth(date->month, date->year) == 29) 536 | { 537 | date->day = 1; 538 | hdateaddmonth(date, 1); 539 | } else { 540 | date->day++; 541 | } 542 | daycount--; 543 | break; 544 | default: 545 | date->day++; 546 | daycount--; 547 | break; 548 | } 549 | } 550 | while (daycount < 0) 551 | { 552 | switch (date->day) 553 | { 554 | case 1: 555 | hdateaddmonth(date, -1); 556 | if (LastDayOfHebrewMonth(date->month, date->year) == 30) 557 | { 558 | date->day = 30; 559 | } else { 560 | date->day = 29; 561 | } 562 | daycount++; 563 | break; 564 | default: 565 | date->day--; 566 | daycount++; 567 | break; 568 | } 569 | } 570 | hdatesetdoy(date); 571 | } 572 | 573 | void divideandcarry(int start, int *finish, int *carry, int divisor) 574 | { 575 | *finish = start%divisor; 576 | *carry = start/divisor; 577 | if (*finish < 0) 578 | { 579 | *finish +=divisor; 580 | --*carry; 581 | } 582 | } 583 | 584 | void hdateaddhour(hdate *date, int hours) 585 | { 586 | int hour = date->hour + hours; 587 | int carry = 0; 588 | divideandcarry(hour, &date->hour, &carry, 24); 589 | if (carry){hdateaddday(date, carry);} 590 | else {hdatesetdoy(date);} 591 | } 592 | 593 | void hdateaddminute(hdate *date, int minutes) 594 | { 595 | int minute = date->min + minutes; 596 | int carry = 0; 597 | divideandcarry(minute, &date->min, &carry, 60); 598 | if (carry){hdateaddhour(date, carry);} 599 | else {hdatesetdoy(date);} 600 | } 601 | 602 | void hdateaddsecond(hdate *date, int seconds) 603 | { 604 | int second = date->sec + seconds; 605 | int carry = 0; 606 | divideandcarry(second, &date->sec, &carry, 60); 607 | if (carry){hdateaddminute(date, carry);} 608 | else {hdatesetdoy(date);} 609 | } 610 | 611 | void hdateaddmsecond(hdate *date, int mseconds) 612 | { 613 | int msecond = date->msec + mseconds; 614 | int carry = 0; 615 | divideandcarry(msecond, &date->msec, &carry, 1000); 616 | if (carry){hdateaddsecond(date, carry);} 617 | else {hdatesetdoy(date);} 618 | } 619 | 620 | void hdateadd(hdate *date, int years, int months, int days, int hours, int minutes, int seconds, int mseconds) 621 | { 622 | if (years) {hdateaddyear(date, years);} 623 | if (months) {hdateaddmonth(date, months);} 624 | if (days) {hdateaddday(date, days);} 625 | if (hours) {hdateaddhour(date, hours);} 626 | if (minutes) {hdateaddminute(date, minutes);} 627 | if (seconds) {hdateaddsecond(date, seconds);} 628 | if (mseconds) {hdateaddmsecond(date, mseconds);} 629 | } 630 | 631 | molad getmolad(int year, int month) 632 | { 633 | hdate result ={0}; 634 | int MonthsElapsed = 635 | (235 * ((year - 1) / 19)) // Months in complete cycles so far. 636 | + (12 * ((year - 1) % 19)) // Regular months in this cycle. 637 | + (7 * ((year - 1) % 19) + 1) / 19; // Leap months this cycle 638 | if(month > 6) 639 | { 640 | MonthsElapsed += (month-7); 641 | } else { 642 | MonthsElapsed += (month+5); 643 | if (HebrewLeapYear(year)){MonthsElapsed += 1;} 644 | } 645 | 646 | long int PartsElapsed = 204 + 793 * (MonthsElapsed % 1080); 647 | long int HoursElapsed = 648 | 5 + 12 * MonthsElapsed + 793 * (MonthsElapsed / 1080) 649 | + PartsElapsed / 1080; 650 | long int ConjunctionDay = 29 * MonthsElapsed + (HoursElapsed) / 24; 651 | int ConjunctionHour =(HoursElapsed % 24); 652 | int ConjunctionMinute = (PartsElapsed % 1080) / 18; 653 | int ConjunctionParts = (PartsElapsed % 1080) % 18; 654 | result.year = 1; 655 | result.month = 7; 656 | result.day = 1; 657 | hdateaddday(&result, ConjunctionDay); 658 | result.hour = ConjunctionHour; 659 | result.min = ConjunctionMinute; 660 | result.sec = ConjunctionParts; 661 | result.offset = 8456; 662 | hdateaddhour(&result, -6); 663 | return result; 664 | } 665 | 666 | int getYearType(hdate date) 667 | { 668 | int yearWday = (HebrewCalendarElapsedDays(date.year)+1)%7; 669 | if (date.leap) 670 | { 671 | switch (yearWday) 672 | { 673 | case 2: 674 | if (ShortKislev(date.year)) 675 | { 676 | if (date.EY) { return 14;} 677 | return 6; 678 | } 679 | if (LongHeshvan(date.year)) 680 | { 681 | if (date.EY) { return 15;} 682 | return 7; 683 | } 684 | break; 685 | case 3: 686 | if (date.EY) { return 15;} 687 | return 7; 688 | break; 689 | case 5: 690 | if (ShortKislev(date.year)) {return 8;} 691 | if (LongHeshvan(date.year)) {return 9;} 692 | break; 693 | case 0: 694 | if (ShortKislev(date.year)) {return 10;} 695 | if (LongHeshvan(date.year)) 696 | { 697 | if (date.EY) { return 16;} 698 | return 11; 699 | } 700 | break; 701 | } 702 | } else { 703 | switch (yearWday) 704 | { 705 | case 2: 706 | if (ShortKislev(date.year)) {return 0;} 707 | if (LongHeshvan(date.year)) 708 | { 709 | if (date.EY) { return 12;} 710 | return 1; 711 | } 712 | break; 713 | case 3: 714 | if (date.EY) { return 12;} 715 | return 1; 716 | break; 717 | case 5: 718 | if (LongHeshvan(date.year)) {return 3;} 719 | if (!ShortKislev(date.year)) 720 | { 721 | if (date.EY) { return 13;} 722 | return 2; 723 | } 724 | break; 725 | case 0: 726 | if (ShortKislev(date.year)) {return 4;} 727 | if (LongHeshvan(date.year)) {return 5;} 728 | break; 729 | } 730 | } 731 | return -1; 732 | } 733 | 734 | parshah getparshah(hdate date) 735 | { 736 | int yearType = getYearType(date); 737 | //optimise 738 | int yearWday = HebrewCalendarElapsedDays(date.year)%7; 739 | int day = yearWday + date.dayofyear; 740 | if (date.wday) {return NOPARSHAH;} 741 | if (yearType >= 0) 742 | { 743 | return parshahlist[yearType][day/7]; 744 | } 745 | return NOPARSHAH; 746 | } 747 | 748 | yomtov getyomtov(hdate date) 749 | { 750 | switch(date.month) 751 | { 752 | case 1: 753 | if(date.day == 14) {return EREV_PESACH;} 754 | if(date.day == 15) {return PESACH_DAY1;} 755 | if(date.day == 16 && date.EY) {return CHOL_HAMOED_PESACH_DAY1;} 756 | if(date.day == 16) {return PESACH_DAY2;} 757 | if(date.day == 17 && date.EY) {return CHOL_HAMOED_PESACH_DAY2;} 758 | if(date.day == 17) {return CHOL_HAMOED_PESACH_DAY1;} 759 | if(date.day == 18 && date.EY) {return CHOL_HAMOED_PESACH_DAY3;} 760 | if(date.day == 18) {return CHOL_HAMOED_PESACH_DAY2;} 761 | if(date.day == 19 && date.EY) {return CHOL_HAMOED_PESACH_DAY4;} 762 | if(date.day == 19) {return CHOL_HAMOED_PESACH_DAY3;} 763 | if(date.day == 20 && date.EY) {return CHOL_HAMOED_PESACH_DAY5;} 764 | if(date.day == 20) {return CHOL_HAMOED_PESACH_DAY4;} 765 | if(date.day == 21) {return SHVEI_SHEL_PESACH;} 766 | if(date.day == 22 && !date.EY) {return ACHRON_SHEL_PESACH;} 767 | break; 768 | case 2: 769 | if(date.day == 14) {return PESACH_SHEINI;} 770 | if(date.day == 18) {return LAG_BAOMER;} 771 | break; 772 | case 3: 773 | if(date.day == 5) {return EREV_SHAVOUS;} 774 | if(date.day == 6) {return SHAVOUS_DAY1;} 775 | if(date.day == 7 && !date.EY) {return SHAVOUS_DAY2;} 776 | break; 777 | case 4: 778 | if(date.day == 17 && date.wday) {return SHIVA_ASAR_BTAAMUZ;} 779 | if(date.day == 18 && date.wday == 1) {return SHIVA_ASAR_BTAAMUZ;} 780 | break; 781 | case 5: 782 | if(date.day == 9 && date.wday) {return TISHA_BAV;} 783 | if(date.day == 10 && date.wday == 1) {return TISHA_BAV;} 784 | if(date.day == 15) {return TU_BAV;} 785 | break; 786 | case 6: 787 | if(date.day == 29) {return EREV_ROSH_HASHANAH;} 788 | break; 789 | case 7: 790 | if(date.day == 1) {return ROSH_HASHANAH_DAY1;} 791 | if(date.day == 2) {return ROSH_HASHANAH_DAY2;} 792 | if(date.day == 3 && date.wday) {return TZOM_GEDALIA;} 793 | if(date.day == 4 && date.wday == 1) {return TZOM_GEDALIA;} 794 | if(date.day == 9) {return EREV_YOM_KIPPUR;} 795 | if(date.day == 10) {return YOM_KIPPUR;} 796 | if(date.day == 14) {return EREV_SUKKOS;} 797 | if(date.day == 15) {return SUKKOS_DAY1;} 798 | if(date.day == 16 && date.EY) {return CHOL_HAMOED_SUKKOS_DAY1;} 799 | if(date.day == 16) {return SUKKOS_DAY2;} 800 | if(date.day == 17 && date.EY) {return CHOL_HAMOED_SUKKOS_DAY2;} 801 | if(date.day == 17) {return CHOL_HAMOED_SUKKOS_DAY1;} 802 | if(date.day == 18 && date.EY) {return CHOL_HAMOED_SUKKOS_DAY3;} 803 | if(date.day == 18) {return CHOL_HAMOED_SUKKOS_DAY2;} 804 | if(date.day == 19 && date.EY) {return CHOL_HAMOED_SUKKOS_DAY4;} 805 | if(date.day == 19) {return CHOL_HAMOED_SUKKOS_DAY3;} 806 | if(date.day == 20 && date.EY) {return CHOL_HAMOED_SUKKOS_DAY5;} 807 | if(date.day == 20) {return CHOL_HAMOED_SUKKOS_DAY4;} 808 | if(date.day == 21) {return HOSHANA_RABBAH;} 809 | if(date.day == 22) {return SHMEINI_ATZERES;} 810 | if(date.day == 23 && !date.EY) {return SIMCHAS_TORAH;} 811 | break; 812 | case 9: 813 | if(date.day == 25) {return CHANUKAH_DAY1;} 814 | if(date.day == 26) {return CHANUKAH_DAY2;} 815 | if(date.day == 27) {return CHANUKAH_DAY3;} 816 | if(date.day == 28) {return CHANUKAH_DAY4;} 817 | if(date.day == 29) {return CHANUKAH_DAY5;} 818 | if(date.day == 30) {return CHANUKAH_DAY6;} 819 | break; 820 | case 10: 821 | if(date.day == 1) 822 | { if(ShortKislev(date.year)) {return CHANUKAH_DAY6;} 823 | else {return CHANUKAH_DAY7;}} 824 | if(date.day == 2) 825 | { if(ShortKislev(date.year)) {return CHANUKAH_DAY7;} 826 | else {return CHANUKAH_DAY8;}} 827 | if(date.day == 3 && ShortKislev(date.year)) {return CHANUKAH_DAY8;} 828 | if(date.day == 10) {return ASARAH_BTEVES;} 829 | break; 830 | case 11: 831 | if(date.day == 15) {return TU_BISHVAT;} 832 | break; 833 | case 12: 834 | if(!date.leap && date.day == 11 && date.wday == 5) {return TAANIS_ESTER;} 835 | if(!date.leap && date.day == 13 && date.wday) {return TAANIS_ESTER;} 836 | if(date.day == 14) 837 | { if(date.leap) {return PURIM_KATAN;} 838 | else {return PURIM;}} 839 | if(date.day == 15) 840 | { if(date.leap) {return SHUSHAN_PURIM_KATAN;} 841 | else {return SHUSHAN_PURIM;}} 842 | break; 843 | case 13: 844 | if(date.day == 11 && date.wday == 5) {return TAANIS_ESTER;} 845 | if(date.day == 13 && date.wday) {return TAANIS_ESTER;} 846 | if(date.day == 14) {return PURIM;} 847 | if(date.day == 15) {return SHUSHAN_PURIM;} 848 | break; 849 | } 850 | return CHOL; 851 | } 852 | 853 | yomtov getspecialshabbos(hdate date) 854 | { 855 | if(!date.wday) 856 | { 857 | if((date.month == 11 && !date.leap) || (date.month == 12 && date.leap)) 858 | { 859 | if(date.day == 25 860 | || date.day == 27 861 | || date.day == 29) 862 | {return SHKALIM;} 863 | } 864 | if((date.month == 12 && !date.leap) || date.month == 13) 865 | { 866 | if(date.day == 1) {return SHKALIM;} 867 | if(date.day == 8 868 | || date.day == 9 869 | || date.day == 11 870 | || date.day == 13) 871 | {return ZACHOR;} 872 | if(date.day == 18 873 | || date.day == 20 874 | || date.day == 22 875 | || date.day == 23) 876 | {return PARAH;} 877 | if(date.day == 25 878 | || date.day == 27 879 | || date.day == 29) 880 | {return HACHODESH;} 881 | } 882 | if(date.month == 1 && date.day == 1) {return HACHODESH;} 883 | } 884 | return CHOL; 885 | } 886 | 887 | yomtov getroshchodesh(hdate date) 888 | { 889 | if (date.day == 30 890 | || (date.day == 1 && date.month != 7)) 891 | {return ROSH_CHODESH;} 892 | return CHOL; 893 | } 894 | 895 | yomtov getmacharchodesh(hdate date) 896 | { 897 | if (date.wday) {return CHOL;} 898 | if (date.day == 30 || date.day == 29) {return MACHAR_CHODESH;} 899 | return CHOL; 900 | } 901 | 902 | yomtov getshabbosmevorchim(hdate date) 903 | { 904 | if (date.wday) {return CHOL;} 905 | if (date.day >= 23 && date.day <= 29) {return SHABBOS_MEVORCHIM;} 906 | return CHOL; 907 | } 908 | 909 | int getomer(hdate date) 910 | { 911 | int omer = 0; 912 | if (date.month == 1 && date.day >= 16) {omer = date.day - 15;} 913 | if (date.month == 2) {omer = date.day + 15;} 914 | if (date.month == 3 && date.day <= 5) {omer = date.day + 44;} 915 | return omer; 916 | } 917 | 918 | int getavos(hdate date) 919 | { 920 | if (date.wday) {return 0;} // Shabbos 921 | int avos_start = nissanCount(date.year) + 23; // 23 Nissan 922 | int avos_day = date.dayofyear - avos_start; // days from start of avos 923 | if (avos_day <= 0) {return 0;} 924 | int chapter = (avos_day/7); // current chapter 925 | // corrections 926 | int avos_day_of_week = avos_day%7; 927 | if (avos_day_of_week == 6) { // tisha bav is Shabbos 928 | if (avos_day == 104) {return 0;} // tisha bav 929 | if (avos_day > 104) {chapter -=1;} // after tisha bav 930 | } else if (avos_day_of_week == 1 && (! date.EY)){// 2nd day of Shavous is Shabbos in Chutz Laaretz 931 | if (avos_day == 43) {return 0;} // Shavous 932 | if (avos_day > 43) {chapter -=1;} // after Shavous 933 | } 934 | // normalize 935 | chapter %=6; 936 | chapter +=1; 937 | // Elul double chapters 938 | if (date.month == 6) 939 | { 940 | if (date.day > 22) { chapter = 56;} // Last week Chapter 5 - 6 941 | else if (date.day > 15) { chapter = 34;} // Chapter 3 - 4 942 | else if (date.day > 8 && chapter == 1) { chapter = 12;} // Chapter 1 - 2 943 | } 944 | return chapter; 945 | } 946 | _Bool istaanis(hdate date) 947 | { 948 | yomtov current = getyomtov(date); 949 | if (current == YOM_KIPPUR 950 | || (current >= SHIVA_ASAR_BTAAMUZ && current <= TAANIS_ESTER)) 951 | {return 1;} 952 | return 0; 953 | } 954 | 955 | _Bool isassurbemelachah(hdate date) 956 | { 957 | yomtov current = getyomtov(date); 958 | if(!date.wday 959 | || (current >= PESACH_DAY1 && current <= SIMCHAS_TORAH)) 960 | {return 1;} 961 | return 0; 962 | } 963 | 964 | int iscandlelighting(hdate date) 965 | { 966 | if(date.wday == 6) {return 1;} 967 | yomtov current = getyomtov(date); 968 | if((current >= EREV_PESACH && current <= EREV_SUKKOS) 969 | || (current == CHOL_HAMOED_PESACH_DAY4 && !date.EY) 970 | || (current == CHOL_HAMOED_PESACH_DAY5 && date.EY) 971 | || current == HOSHANA_RABBAH) 972 | { 973 | if(!date.wday){return 2;} 974 | return 1; 975 | } 976 | if(current == PESACH_DAY1 977 | || current == SHVEI_SHEL_PESACH 978 | || current == SHAVOUS_DAY1 979 | || current == ROSH_HASHANAH_DAY1 980 | || current == SUKKOS_DAY1 981 | || current == SHMEINI_ATZERES){return 2;} 982 | if((current == ACHRON_SHEL_PESACH 983 | || current == SHAVOUS_DAY2 984 | || current == ROSH_HASHANAH_DAY2 985 | || current == SIMCHAS_TORAH) 986 | && date.wday == 6) {return 2;} 987 | if((date.month == 9 && date.day == 24) 988 | || (current >= CHANUKAH_DAY1 && current <= CHANUKAH_DAY7)) 989 | { 990 | if(!date.wday){return 2;} 991 | return 3; 992 | } 993 | return 0; 994 | } 995 | 996 | _Bool isbirchashachama(hdate date) 997 | { 998 | int yearstart = HebrewCalendarElapsedDays(date.year); 999 | int day = yearstart + date.dayofyear; 1000 | if (day%10227 == 172){return 1;} 1001 | return 0; 1002 | } 1003 | 1004 | int TekufasTishreiElapsedDays(hdate date) 1005 | { 1006 | /* 1007 | Tekufas Shmuel: a solar year is 365.25 days. 1008 | notation: days,hours,chalakim 1009 | molad BaHaRad was 2D,5H,204C 1010 | or 5H,204C from the start of rosh hashana year 1 1011 | molad nissan add 177D,4H,438C (6 * 29D,12H,793C) 1012 | or 177D,9H,642C after rosh hashana year 1 1013 | tekufas nissan was 7D,9H,642C before molad nissan ~rambam. 1014 | or 170D,0H,0C after rosh hashana year 1 1015 | tekufas tishrei was 182D,3H (365.25 / 2) before tekufas nissan 1016 | or 12D,15H before Rosh Hashana year 1 1017 | outside of EY we say תל ומטר in ברכת השנים from 60 days after tekufas tishrei. 1018 | 60 includes the day of the tekufah and the day we start. 1019 | 60 days from the tekufah == 47D,9H from Rosh Hashana year 1 1020 | */ 1021 | 1022 | // days since Rosh Hashana year 1 1023 | // add 1/2 day as the first tekufas tishrei was 9 hours into the day 1024 | // this allows all 4 years of the secular leap year cycle to share 47 days 1025 | // make from 47D,9H to 47D for simplicity 1026 | double days = HebrewCalendarElapsedDays(date.year) + (date.dayofyear-1) + .5; 1027 | // days of completed solar years 1028 | double solar = (date.year-1)*365.25; 1029 | return (int) days - solar; 1030 | } 1031 | 1032 | _Bool isbirchashashanim(hdate date) 1033 | { 1034 | if (date.EY && date.month == 7 && date.day == 7) {return 1;} 1035 | else if (TekufasTishreiElapsedDays(date) == 47) {return 1;} 1036 | return 0; 1037 | } 1038 | 1039 | _Bool getbirchashashanim(hdate date) 1040 | { 1041 | if (date.month == 1 && date.day < 15) {return 1;} 1042 | if (date.month < 7) {return 0;} 1043 | if (date.EY) 1044 | { 1045 | if (date.month == 7 && date.day < 7) {return 0;} 1046 | else {return 1;} 1047 | } else { 1048 | if (TekufasTishreiElapsedDays(date) < 47) {return 0;} 1049 | else {return 1;} 1050 | } 1051 | } -------------------------------------------------------------------------------- /esphome/header-files/melachaplug_main.h: -------------------------------------------------------------------------------- 1 | /**** 2 | Copyright (c) 2023 RebbePod 3 | 4 | This library is free software; you can redistribute it and/or modify it 5 | under the terms of the GNU Lesser GeneralPublic License as published by the Free Software Foundation; 6 | either version 2.1 of the License, or (at your option) any later version. 7 | 8 | This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; 9 | without even the impliedwarranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | See the GNU Lesser General Public License for more details. 11 | You should have received a copy of the GNU Lesser General Public License along with this library; 12 | if not, write tothe Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA, 13 | or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 14 | ****/ 15 | 16 | #pragma once 17 | 18 | //#include "../config/esphome/header-files/hebrewcalender_melachaplug.h" 19 | #include "esphome.h" 20 | using namespace esphome; 21 | using namespace time; 22 | 23 | namespace Hdate { 24 | hdate current_hdate; // global varible for todays hebrew date 25 | } // namespace Hdate 26 | 27 | namespace MelachaPlug { 28 | 29 | time_t getPlagTime() { 30 | ESP_LOGD("getPlagTime", "getPlagTime ran"); 31 | esphome::ESPTime date = id(sntp_time).now(); 32 | date.hour = date.minute = date.second = 0; // uptime time to 00:00:00 33 | date.recalc_timestamp_utc(); 34 | auto sunrise_today = id(mysun).sunrise(date, -1.583); // time of hanetz amiti Alter Rebbe 35 | auto sunset_today = id(mysun).sunset(date, -1.583); // time of shkiah amitis Alter Rebbe 36 | float shaahzmanis = (sunset_today->timestamp - sunrise_today->timestamp) / 12; 37 | time_t plag = sunrise_today->timestamp + (shaahzmanis * 10.75); // time of plag Alter Rebbe 38 | 39 | ESP_LOGD("getPlagTime", "sunrise_today (amiti): %lu", sunrise_today->timestamp); 40 | ESP_LOGD("getPlagTime", "sunset_today (amiti): %lu", sunset_today->timestamp); 41 | ESP_LOGD("getPlagTime", "%s", sunrise_today->strftime("sunrise_today: --- %a, %b %e, %Y, %l:%M %p ---").c_str()); 42 | ESP_LOGD("getPlagTime", "%s", sunset_today->strftime("sunset_today: --- %a, %b %e, %Y, %l:%M %p ---").c_str()); 43 | ESP_LOGD("getPlagTime", "shaahzmanis: %f", shaahzmanis); 44 | ESP_LOGD("getPlagTime", "plag timestamp: %lu", plag); 45 | return plag; 46 | } 47 | 48 | void setEretzYisrael() { 49 | if (id(eretz_yisrael).state == true) { 50 | setEY(&Hdate::current_hdate, true); 51 | ESP_LOGD("setEretzYisrael", "EY set to true"); 52 | } else { 53 | setEY(&Hdate::current_hdate, false); 54 | ESP_LOGD("setEretzYisrael", "EY set to false"); 55 | } 56 | } 57 | 58 | void updateHdate() { 59 | // only run once we have the proper time and location 60 | ESP_LOGD("updateHdate", "updateHdate ran"); 61 | if (id(time_valid) == false) { 62 | ESP_LOGD("updateHdate", "date not valid"); 63 | return; 64 | } 65 | float elevation_deg; 66 | int offset_minutes; 67 | time_t timestamp_sunset; 68 | 69 | esphome::ESPTime date = id(sntp_time).now(); // we use this variable later 70 | time_t timestamp_now = date.timestamp; // save the current timestamp before updating the date 71 | struct tm tm_time_now = date.to_c_tm(); 72 | Hdate::current_hdate = convertDate(tm_time_now); // convert current english date to the hebrew date 73 | MelachaPlug::setEretzYisrael(); // confirm EY is set correctly 74 | 75 | // check which degree and offset we calculate from 76 | if (isassurbemelachah(Hdate::current_hdate) == false) { 77 | ESP_LOGD("updateHdate", "since its a weeekday, we calculate hebrew day starting from Shabbos start time"); 78 | elevation_deg = id(deg_shabbos_starts).state; 79 | offset_minutes = id(min_offset_start).state; // use the start time offset 80 | } else { 81 | ESP_LOGD("updateHdate", "since its Shabbos or Yom Tov, we calculate hebrew day starting from Shabbos end time"); 82 | elevation_deg = id(deg_shabbos_ends).state; 83 | offset_minutes = id(min_offset_end).state; // use the end time offset 84 | } 85 | 86 | // check if using early shabbos and use plag as start time 87 | if (id(early_shabbos).state == true && isassurbemelachah(Hdate::current_hdate) == false) { 88 | ESP_LOGD("updateHdate", "Early Shabbos Switch is ON use plag as the start time"); 89 | timestamp_sunset = getPlagTime(); 90 | } else { 91 | ESP_LOGD("updateHdate", "using elevation_deg to calculate shabbos start/end time"); 92 | date.hour = date.minute = date.second = 0; // uptime time to 00:00:00 93 | date.recalc_timestamp_utc(); 94 | auto sunset_today = id(mysun).sunset(date, elevation_deg); // get sunset time at specified degree 95 | ESP_LOGD("updateHdate", "%s", sunset_today->strftime("sunset_today: --- %a, %b %e, %Y, %l:%M %p ---").c_str()); 96 | timestamp_sunset = sunset_today->timestamp; 97 | } 98 | ESP_LOGD("updateHdate", "timestamp_now: %lu", timestamp_now); 99 | ESP_LOGD("updateHdate", "timestamp_sunset before offset applied: %lu", timestamp_sunset); 100 | timestamp_sunset += offset_minutes * 60; // apply minute offset to sunset 101 | ESP_LOGD("updateHdate", "timestamp_sunset after offset applied: %lu", timestamp_sunset); 102 | 103 | if (timestamp_now > timestamp_sunset) { 104 | ESP_LOGD("updateHdate", "it's between midnight and sunset adding a day to Hdate"); 105 | hdateaddday(&Hdate::current_hdate, 1); // advance hdate 106 | } else { 107 | ESP_LOGD("updateHdate", "it's not between midnight and sunset"); 108 | } 109 | id(hdate_valid) = true; // let the global varibale know that we updated the date 110 | 111 | date = date.from_epoch_local(timestamp_sunset); 112 | ESP_LOGD("updateHdate", "timestamp_sunset: %lu", date.timestamp); 113 | id(todays_melacha_check_time).publish_state( date.strftime("%a, %b %e, %l:%M %p") ); 114 | } 115 | 116 | void publishHebrewDay() { 117 | std::string hebrew_month; 118 | switch(Hdate::current_hdate.month) { 119 | case 1: 120 | hebrew_month = "Nissan"; 121 | break; 122 | case 2: 123 | hebrew_month = "Iyar"; 124 | break; 125 | case 3: 126 | hebrew_month = "Sivan"; 127 | break; 128 | case 4: 129 | hebrew_month = "Tammuz"; 130 | break; 131 | case 5: 132 | hebrew_month = "Av"; 133 | break; 134 | case 6: 135 | hebrew_month = "Elul"; 136 | break; 137 | case 7: 138 | hebrew_month = "Tishrei"; 139 | break; 140 | case 8: 141 | hebrew_month = "Cheshvan"; 142 | break; 143 | case 9: 144 | hebrew_month = "Kislev"; 145 | break; 146 | case 10: 147 | hebrew_month = "Tevet"; 148 | break; 149 | case 11: 150 | hebrew_month = "Shevat"; 151 | break; 152 | case 12: 153 | if (Hdate::current_hdate.leap) { 154 | hebrew_month = "Adar I"; 155 | } else { 156 | hebrew_month = "Adar"; 157 | } 158 | break; 159 | case 13: 160 | hebrew_month = "Adar II"; 161 | break; 162 | default: 163 | ESP_LOGD("publishHebrewDay", "Month not found in hebrew date!"); 164 | } 165 | 166 | id(hebrew_date).publish_state( to_string(Hdate::current_hdate.day) + " " + hebrew_month + " " + to_string(Hdate::current_hdate.year) ); 167 | ESP_LOGD("publishHebrewDay", "Year: %d Month: %s Day: %d", Hdate::current_hdate.year, hebrew_month.c_str(), Hdate::current_hdate.day); 168 | } 169 | 170 | void setAlterRebbeZmanim() { 171 | // reverts to default Alter Rebbe zmanin 172 | id(deg_shabbos_starts).publish_state(-0.833); 173 | id(deg_shabbos_ends).publish_state(-8.5); 174 | } 175 | 176 | void updateTextSensors() { 177 | id(ts_sunset).update(); 178 | id(ts_tzeit).update(); 179 | } 180 | 181 | void setShabbosMode() { 182 | // should only be run once were are certain the hebrew date is correct 183 | ESP_LOGD("setShabbosMode", "setShabbosMode ran"); 184 | if (isassurbemelachah(Hdate::current_hdate) == false) { 185 | ESP_LOGD("setShabbosMode", "melacha permitted"); 186 | // weekday, turn off Shabbos mode 187 | if (id(override_shabbos_mode).state == false) { 188 | id(shabbos_mode).turn_off(); // turn off Shabbos mode 189 | ESP_LOGD("setShabbosMode", "turned off Shabbos Mode"); 190 | } 191 | } else { 192 | ESP_LOGD("setShabbosMode", "melacha prohibited"); 193 | // Shabbos or Yom Tov, turn on Shabbos mode 194 | if (id(override_shabbos_mode).state == false) { 195 | id(shabbos_mode).turn_on(); // turn on Shabbos mode 196 | ESP_LOGD("setShabbosMode", "turned on Shabbos Mode"); 197 | } 198 | } 199 | } 200 | 201 | // ---------------------------------------- On Sequences ---------------------------------------- 202 | void runMelachaChecks() { 203 | ESP_LOGD("runMelachaChecks", "runMelachaChecks ran"); 204 | MelachaPlug::updateHdate(); // update the hebrew date 205 | MelachaPlug::setShabbosMode(); // checks if issur and turns on Shabbos mode 206 | MelachaPlug::publishHebrewDay(); // update the hebrew day text sensor 207 | MelachaPlug::updateTextSensors(); 208 | } 209 | } // namespace MelachaPlug 210 | 211 | 212 | namespace LocationExtras { 213 | 214 | void detectLocation() { 215 | WiFiClient wifiClient; 216 | HTTPClient http; 217 | http.begin(wifiClient, "http://ip-api.com/json/?fields=status,regionName,city,zip,lat,lon"); // API from http://ip-api.com/ 218 | int httpResponseCode = http.GET(); 219 | std::string payload = http.getString().c_str(); 220 | ESP_LOGD("detectLocation", "Curernt Location: %s", payload.c_str()); 221 | http.end(); 222 | 223 | int start = payload.find("status") + 9; 224 | int end = payload.find(",", start) - 1; 225 | std::string status = payload.substr(start, end - start); 226 | 227 | if(status == "success") { 228 | start = payload.find("regionName") + 13; 229 | end = payload.find(",", start) - 1; 230 | std::string state = payload.substr(start, end - start); 231 | 232 | start = payload.find("city") + 7; 233 | end = payload.find(",", start) - 1; 234 | std::string city = payload.substr(start, end - start); 235 | 236 | start = payload.find("zip") + 6; 237 | end = payload.find(",", start) - 1; 238 | std::string zip = payload.substr(start, end - start); 239 | 240 | start = payload.find("lat") + 5; 241 | end = payload.find(",", start); 242 | std::string lat = payload.substr(start, end - start); 243 | // float lat = parse_float(payload.substr(start, end - start)).value(); 244 | 245 | start = payload.find("lon") + 5; 246 | end = payload.find("}", start); // it's the end of the json therefor look for '}' 247 | std::string lon = payload.substr(start, end - start); 248 | // float lon = parse_float(payload.substr(start, end - start)).value(); 249 | 250 | ESP_LOGD("detectLocation", "status: %s, state: %s, city: %s, zip: %s, lat: %s, lon: %s", status.c_str(), state.c_str(), city.c_str(), zip.c_str(), lat.c_str(), lon.c_str()); 251 | id(latitude_number).publish_state(atof(lat.c_str())); 252 | id(longitude_number).publish_state(atof(lon.c_str())); 253 | id(location_info).publish_state("Auto detected: " + city + ", " + state + " " + zip); 254 | } else { 255 | id(location_info).publish_state("Failed to get your location"); 256 | ESP_LOGD("detectLocation", "Failed to get your location"); 257 | } 258 | } 259 | 260 | void revertToDefaultLocation(double latitude, double longitude) { 261 | // reverts to default location 262 | ESP_LOGD("revertToDefaultLocation", "latitude: %f longitude: %f", latitude, longitude); 263 | if(latitude == 40.669000) { 264 | // same as default 770 location. we change some digits so it shouldn't 265 | // trigger the auto location detection feature. See LocationExtras::onBoot() 266 | ESP_LOGD("revertToDefaultLocation", "same as default 770 location"); 267 | id(latitude_number).publish_state(40.668888f); 268 | id(location_info).publish_state("Using 770 location"); 269 | } else { 270 | ESP_LOGD("revertToDefaultLocation", "using exact coded latitude"); 271 | id(latitude_number).publish_state(latitude); 272 | id(location_info).publish_state("Using coded location"); 273 | } 274 | id(longitude_number).publish_state(longitude); 275 | } 276 | 277 | // ---------------------------------------- On Sequences ---------------------------------------- 278 | 279 | void onBoot(double latitude, double longitude) { 280 | ESP_LOGD("onBoot", "--------- sun elevation: %f", id(mysun).elevation() ); 281 | ESP_LOGD("onBoot", "lat: %f lon: %f", id(latitude_number).state, id(latitude_number).state ); 282 | ESP_LOGD("onBoot", "latitude: %f longitude: %f", latitude, longitude); 283 | 284 | if (id(latitude_number).state == 40.669000 && id(longitude_number).state == -73.942770) { 285 | // default coded location hasn't been updated, let's auto detect the location 286 | LocationExtras::detectLocation(); 287 | ESP_LOGD("onBoot", "Using auto location"); 288 | } else { 289 | // otherwise use the number variable we have set 290 | if (id(latitude_number).state == latitude && id(longitude_number).state == longitude) { 291 | id(location_info).publish_state("Using coded location"); 292 | ESP_LOGD("onBoot", "Using coded location"); 293 | } else { 294 | id(location_info).publish_state("Using saved location"); 295 | ESP_LOGD("onBoot", "Using saved location"); 296 | } 297 | } 298 | } 299 | 300 | void onLatitudeSet() { 301 | ESP_LOGD("onLatitudeSet", "onLatitudeSet ran"); 302 | id(mysun).set_latitude(id(latitude_number).state); 303 | id(sntp_time).update(); // will also update hebrew date and check date 304 | } 305 | 306 | void onLongitudeSet() { 307 | ESP_LOGD("onLongitudeSet", "onLongitudeSet ran"); 308 | id(mysun).set_longitude(id(longitude_number).state); 309 | id(sntp_time).update(); // will also update hebrew date and check date 310 | } 311 | } // namespace LocationExtras -------------------------------------------------------------------------------- /esphome/melachaplug-8285.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #------------ User Configuration ------------ 3 | #-------------------------------------------- 4 | wifi: 5 | ssid: !secret wifi_ssid5 # switch "!secret wifi_ssid5" to your wifi ssid 6 | password: !secret wifi_password5 # switch "!secret wifi_password5" to your wifi password 7 | 8 | # Switch this section based on your chip 9 | esp8266: 10 | board: esp8285 11 | restore_from_flash: true 12 | # esp32: 13 | # board: esp32doit-devkit-v1 14 | 15 | substitutions: 16 | device_name: "melachaplug-8285" # access web UI using "device_name".local/ or it's IP 17 | friendly_name: "Melacha Plug 8285" 18 | 19 | relay_1_gpio: "GPIO13" # Switch this section 20 | button_1_gpio: "GPIO3" # based on your chip 21 | built_in_led_gpio: "GPIO4" 22 | led_inverted: "true" 23 | 24 | relay_restore_mode: "ALWAYS_OFF" # relay and led state on startup, change to ALWAYS_ON, 25 | inverted_shabbos_mode: "RESTORE_DEFAULT_OFF" # to default relay on for Shabbos see esphome.io/components/switch/ 26 | 27 | timezone: America/New_York # check format at en.wikipedia.org/wiki/List_of_tz_database_time_zones 28 | eretz_yisrael: "RESTORE_DEFAULT_OFF" # switch to RESTORE_DEFAULT_ON if located in EY and do a one day Yom Tov 29 | 30 | latitude: "40.669000" # change to your location (important for proper zmanim) default: 40.669000 31 | longitude: "-73.942770" # 770 Chabad Lubavitch World Headquarters -73.942770 32 | 33 | min_offset_start: "-30" # offset in minutes (on Shabbos start options are: -300 - 120, on end: -120 - 120) 34 | min_offset_end: "0" # use negative number for an early offset (-30) and positive for a later offset 35 | 36 | deg_shabbos_starts: "-0.833" # shkiah is at -0.833 37 | deg_shabbos_ends: "-8.5" # time based off Alter Rebbe www.chabad.org/3209349 38 | deg_tzeit: "-6" 39 | 40 | esphome: 41 | includes: 42 | - header-files/melachaplug_main.h 43 | 44 | packages: 45 | # Local Packages saved in plugins folder 46 | # melacha_config: !include plugins/melachaplug/melacha_config.yaml # Required 47 | # plug_info: !include plugins/melachaplug/plug_info.yaml # Required 48 | # location_info: !include plugins/melachaplug/location_info.yaml # Required 49 | # optional_config: !include plugins/melachaplug/optional_config.yaml # Optional 50 | 51 | remote_package: 52 | url: https://github.com/chabad-source/melachaplug/ 53 | ref: main # optional 54 | files: 55 | - esphome/plugins/melachaplug/melacha_config.yaml # Required 56 | - esphome/plugins/melachaplug/plug_info.yaml # Required 57 | - esphome/plugins/melachaplug/location_info.yaml # Required 58 | - esphome/plugins/melachaplug/optional_config.yaml # Optional 59 | refresh: 1d # Optional -------------------------------------------------------------------------------- /esphome/melachaplug-cloudfree-p2.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #------------ User Configuration ------------ 3 | #-------------------------------------------- 4 | wifi: 5 | ssid: !secret wifi_ssid5 # switch "!secret wifi_ssid5" to your wifi ssid 6 | password: !secret wifi_password5 # switch "!secret wifi_password5" to your wifi password 7 | 8 | # Switch this section based on your chip 9 | esp8266: 10 | board: esp01_1m 11 | restore_from_flash: true 12 | # esp32: 13 | # board: esp32doit-devkit-v1 14 | 15 | substitutions: 16 | device_name: "melachaplug-cloudfree-p2" # access web UI using "device_name".local/ or it's IP 17 | friendly_name: "Melacha Plug CloudFree P2" 18 | 19 | relay_1_gpio: "4" # Switch this section 20 | button_1_gpio: "13" # based on your chip 21 | built_in_led_gpio: "2" 22 | led_inverted: "true" 23 | 24 | relay_restore_mode: "ALWAYS_OFF" # relay and led state on startup, change to ALWAYS_ON, 25 | inverted_shabbos_mode: "RESTORE_DEFAULT_OFF" # to default relay on for Shabbos see esphome.io/components/switch/ 26 | 27 | timezone: America/New_York # check format at en.wikipedia.org/wiki/List_of_tz_database_time_zones 28 | eretz_yisrael: "RESTORE_DEFAULT_OFF" # switch to RESTORE_DEFAULT_ON if located in EY and do a one day Yom Tov 29 | 30 | latitude: "40.669000" # change to your location (important for proper zmanim) default: 40.669000 31 | longitude: "-73.942770" # 770 Chabad Lubavitch World Headquarters -73.942770 32 | 33 | min_offset_start: "-30" # offset in minutes (on Shabbos start options are: -300 - 120, on end: -120 - 120) 34 | min_offset_end: "0" # use negative number for an early offset (-30) and positive for a later offset 35 | 36 | deg_shabbos_starts: "-0.833" # shkiah is at -0.833 37 | deg_shabbos_ends: "-8.5" # time based off Alter Rebbe www.chabad.org/3209349 38 | deg_tzeit: "-6" 39 | 40 | esphome: 41 | includes: 42 | - header-files/melachaplug_main.h 43 | 44 | packages: 45 | # Local Packages saved in plugins folder 46 | # melacha_config: !include plugins/melachaplug/melacha_config.yaml # Required 47 | # plug_info: !include plugins/melachaplug/plug_info.yaml # Required 48 | # location_info: !include plugins/melachaplug/location_info.yaml # Required 49 | # optional_config: !include plugins/melachaplug/optional_config.yaml # Optional 50 | 51 | remote_package: 52 | url: https://github.com/chabad-source/melachaplug/ 53 | ref: main # optional 54 | files: 55 | - esphome/plugins/melachaplug/melacha_config.yaml # Required 56 | - esphome/plugins/melachaplug/plug_info.yaml # Required 57 | - esphome/plugins/melachaplug/location_info.yaml # Required 58 | - esphome/plugins/melachaplug/optional_config.yaml # Optional 59 | refresh: 1d # Optional -------------------------------------------------------------------------------- /esphome/melachaplug-esp32.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #------------ User Configuration ------------ 3 | #-------------------------------------------- 4 | wifi: 5 | ssid: !secret wifi_ssid5 # switch "!secret wifi_ssid5" to your wifi ssid 6 | password: !secret wifi_password5 # switch "!secret wifi_password5" to your wifi password 7 | 8 | # Switch this section based on your chip 9 | # esp8266: 10 | # board: nodemcuv2 11 | # restore_from_flash: true 12 | esp32: 13 | board: esp32doit-devkit-v1 14 | 15 | substitutions: 16 | device_name: "melacha-plug-esp32" # access web UI using "device_name".local/ or it's IP 17 | friendly_name: "Melacha Plug esp32" 18 | 19 | relay_1_gpio: "13" # Switch this section 20 | button_1_gpio: "4" # based on your chip 21 | built_in_led_gpio: "2" 22 | led_inverted: "false" 23 | 24 | relay_restore_mode: "ALWAYS_OFF" # relay and led state on startup, change to ALWAYS_ON, 25 | inverted_shabbos_mode: "RESTORE_DEFAULT_OFF" # to default relay on for Shabbos see esphome.io/components/switch/ 26 | 27 | timezone: America/New_York # check format at en.wikipedia.org/wiki/List_of_tz_database_time_zones 28 | eretz_yisrael: "RESTORE_DEFAULT_OFF" # switch to RESTORE_DEFAULT_ON if located in EY and do a one day Yom Tov 29 | 30 | latitude: "40.669000" # change to your location (important for proper zmanim) default: 40.669000 31 | longitude: "-73.942770" # 770 Chabad Lubavitch World Headquarters -73.942770 32 | 33 | min_offset_start: "-30" # offset in minutes (on Shabbos start options are: -300 - 120, on end: -120 - 120) 34 | min_offset_end: "0" # use negative number for an early offset (-30) and positive for a later offset 35 | 36 | deg_shabbos_starts: "-0.833" # shkiah is at -0.833 37 | deg_shabbos_ends: "-8.5" # time based off Alter Rebbe www.chabad.org/3209349 38 | deg_tzeit: "-6" 39 | 40 | esphome: 41 | includes: 42 | - header-files/melachaplug_main.h 43 | 44 | packages: 45 | # Local Packages saved in plugins folder 46 | # melacha_config: !include plugins/melachaplug/melacha_config.yaml # Required 47 | # plug_info: !include plugins/melachaplug/plug_info.yaml # Required 48 | # location_info: !include plugins/melachaplug/location_info.yaml # Required 49 | # optional_config: !include plugins/melachaplug/optional_config.yaml # Optional 50 | 51 | remote_package: 52 | url: https://github.com/chabad-source/melachaplug/ 53 | ref: main # optional 54 | files: 55 | - esphome/plugins/melachaplug/melacha_config.yaml # Required 56 | - esphome/plugins/melachaplug/plug_info.yaml # Required 57 | - esphome/plugins/melachaplug/location_info.yaml # Required 58 | - esphome/plugins/melachaplug/optional_config.yaml # Optional 59 | refresh: 1d # Optional -------------------------------------------------------------------------------- /esphome/melachaplug-plf10.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #------------ User Configuration ------------ 3 | #-------------------------------------------- 4 | wifi: 5 | ssid: !secret wifi_ssid5 # switch "!secret wifi_ssid5" to your wifi ssid 6 | password: !secret wifi_password5 # switch "!secret wifi_password5" to your wifi password 7 | 8 | # Switch this section based on your chip 9 | esp8266: 10 | board: esp01_1m 11 | restore_from_flash: true 12 | # esp32: 13 | # board: esp32doit-devkit-v1 14 | 15 | substitutions: 16 | device_name: "melachaplug-plf10" # access web UI using "device_name".local/ or it's IP 17 | friendly_name: "Melacha Plug plf10" 18 | 19 | relay_1_gpio: "GPIO4" # Switch this section 20 | button_1_gpio: "GPIO13" # based on your chip 21 | built_in_led_gpio: "GPIO2" 22 | led_inverted: "true" 23 | 24 | relay_restore_mode: "ALWAYS_OFF" # relay and led state on startup, change to ALWAYS_ON, 25 | inverted_shabbos_mode: "RESTORE_DEFAULT_OFF" # to default relay on for Shabbos see esphome.io/components/switch/ 26 | 27 | timezone: America/New_York # check format at en.wikipedia.org/wiki/List_of_tz_database_time_zones 28 | eretz_yisrael: "RESTORE_DEFAULT_OFF" # switch to RESTORE_DEFAULT_ON if located in EY and do a one day Yom Tov 29 | 30 | latitude: "40.669000" # change to your location (important for proper zmanim) default: 40.669000 31 | longitude: "-73.942770" # 770 Chabad Lubavitch World Headquarters -73.942770 32 | 33 | min_offset_start: "-30" # offset in minutes (on Shabbos start options are: -300 - 120, on end: -120 - 120) 34 | min_offset_end: "0" # use negative number for an early offset (-30) and positive for a later offset 35 | 36 | deg_shabbos_starts: "-0.833" # shkiah is at -0.833 37 | deg_shabbos_ends: "-8.5" # time based off Alter Rebbe www.chabad.org/3209349 38 | deg_tzeit: "-6" 39 | 40 | esphome: 41 | includes: 42 | - header-files/melachaplug_main.h 43 | 44 | packages: 45 | # Local Packages saved in plugins folder 46 | # melacha_config: !include plugins/melachaplug/melacha_config.yaml # Required 47 | # plug_info: !include plugins/melachaplug/plug_info.yaml # Required 48 | # location_info: !include plugins/melachaplug/location_info.yaml # Required 49 | # optional_config: !include plugins/melachaplug/optional_config.yaml # Optional 50 | 51 | remote_package: 52 | url: https://github.com/chabad-source/melachaplug/ 53 | ref: main # optional 54 | files: 55 | - esphome/plugins/melachaplug/melacha_config.yaml # Required 56 | - esphome/plugins/melachaplug/plug_info.yaml # Required 57 | - esphome/plugins/melachaplug/location_info.yaml # Required 58 | - esphome/plugins/melachaplug/optional_config.yaml # Optional 59 | refresh: 1d # Optional 60 | -------------------------------------------------------------------------------- /esphome/melachaplug-s31.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #------------ User Configuration ------------ 3 | #-------------------------------------------- 4 | wifi: 5 | ssid: !secret wifi_ssid5 # switch "!secret wifi_ssid5" to your wifi ssid 6 | password: !secret wifi_password5 # switch "!secret wifi_password5" to your wifi password 7 | 8 | # Switch this section based on your chip 9 | esp8266: 10 | board: esp01_1m 11 | restore_from_flash: true 12 | # esp32: 13 | # board: esp32doit-devkit-v1 14 | 15 | substitutions: 16 | device_name: "melachaplug-s31" # access web UI using "device_name".local/ or it's IP 17 | friendly_name: "Melacha Plug S31" 18 | 19 | relay_1_gpio: "GPIO12" # Switch this section 20 | button_1_gpio: "GPIO0" # based on your chip 21 | built_in_led_gpio: "GPIO13" 22 | led_inverted: "true" 23 | 24 | relay_restore_mode: "ALWAYS_OFF" # relay and led state on startup, change to ALWAYS_ON, 25 | inverted_shabbos_mode: "RESTORE_DEFAULT_OFF" # to default relay on for Shabbos see esphome.io/components/switch/ 26 | 27 | timezone: America/New_York # check format at en.wikipedia.org/wiki/List_of_tz_database_time_zones 28 | eretz_yisrael: "RESTORE_DEFAULT_OFF" # switch to RESTORE_DEFAULT_ON if located in EY and do a one day Yom Tov 29 | 30 | latitude: "40.669000" # change to your location (important for proper zmanim) default: 40.669000 31 | longitude: "-73.942770" # 770 Chabad Lubavitch World Headquarters -73.942770 32 | 33 | min_offset_start: "-30" # offset in minutes (on Shabbos start options are: -300 - 120, on end: -120 - 120) 34 | min_offset_end: "0" # use negative number for an early offset (-30) and positive for a later offset 35 | 36 | deg_shabbos_starts: "-0.833" # shkiah is at -0.833 37 | deg_shabbos_ends: "-8.5" # time based off Alter Rebbe www.chabad.org/3209349 38 | deg_tzeit: "-6" 39 | 40 | esphome: 41 | includes: 42 | - header-files/melachaplug_main.h 43 | 44 | packages: 45 | # Local Packages saved in plugins folder 46 | # melacha_config: !include plugins/melachaplug/melacha_config.yaml # Required 47 | # plug_info: !include plugins/melachaplug/plug_info.yaml # Required 48 | # location_info: !include plugins/melachaplug/location_info.yaml # Required 49 | # optional_config: !include plugins/melachaplug/optional_config.yaml # Optional 50 | 51 | remote_package: 52 | url: https://github.com/chabad-source/melachaplug/ 53 | ref: main # optional 54 | files: 55 | - esphome/plugins/melachaplug/melacha_config.yaml # Required 56 | - esphome/plugins/melachaplug/plug_info.yaml # Required 57 | - esphome/plugins/melachaplug/location_info.yaml # Required 58 | - esphome/plugins/melachaplug/optional_config.yaml # Optional 59 | refresh: 1d # Optional -------------------------------------------------------------------------------- /esphome/plugins/melachaplug/location_info.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #-------------- Location Info --------------- 3 | #-------------------------------------------- 4 | esphome: 5 | on_boot: 6 | - priority: -100.0 # pretty much everything should already be initialized 7 | then: 8 | - logger.log: "--- Waiting for hdate to be valid ---" 9 | - wait_until: 10 | lambda: 'return id(hdate_valid);' 11 | - logger.log: --------- hdate valid ---------" 12 | - lambda: 'LocationExtras::onBoot(${latitude}, ${longitude});' 13 | 14 | # Enable HTTP Requests (for auto location API) 15 | http_request: 16 | useragent: esphome/device 17 | timeout: 10s 18 | verify_ssl: false 19 | 20 | text_sensor: 21 | # text sensor to display the location 22 | - platform: template 23 | name: 12 Location Info 24 | internal: true 25 | id: location_info 26 | update_interval: never 27 | 28 | number: 29 | - platform: template 30 | name: 13 Set Latitude 31 | id: latitude_number 32 | optimistic: true 33 | min_value: -180.0000 34 | max_value: 180.0000 35 | step: .0001 36 | mode: box 37 | restore_value: true 38 | initial_value: ${latitude} 39 | on_value: 40 | - lambda: 'LocationExtras::onLatitudeSet();' 41 | - platform: template 42 | name: 14 Set Longitude 43 | id: longitude_number 44 | optimistic: true 45 | min_value: -180.0000 46 | max_value: 180.0000 47 | step: .0001 48 | mode: box 49 | restore_value: true 50 | initial_value: ${longitude} 51 | on_value: 52 | - lambda: 'LocationExtras::onLongitudeSet();' 53 | 54 | button: 55 | - platform: template 56 | name: 15 Detect Location 57 | on_press: 58 | - lambda: 'LocationExtras::detectLocation();' 59 | - platform: template 60 | name: 16 Revert to Default Location 61 | on_press: 62 | - lambda: 'LocationExtras::revertToDefaultLocation(${latitude}, ${longitude});' 63 | -------------------------------------------------------------------------------- /esphome/plugins/melachaplug/melacha_config.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #-------------- Melacha Config -------------- 3 | #-------------------------------------------- 4 | esphome: 5 | name: ${device_name} 6 | friendly_name: ${friendly_name} 7 | # name_add_mac_suffix: true 8 | project: 9 | name: chabad-source.melachaplug 10 | version: "2025.09.02.0" 11 | on_boot: 12 | - priority: 800 # This is where all hardware initialization of vital components is executed 13 | then: 14 | - switch.turn_on: shabbos_mode 15 | 16 | # frequency of data flushed to the flash 17 | preferences: 18 | flash_write_interval: 2min 19 | 20 | # Enable Over The Air updates 21 | ota: 22 | - platform: esphome 23 | 24 | # Enable fallback hotspot 25 | captive_portal: 26 | 27 | wifi: 28 | # Configure fallback hotspot (captive portal) in case wifi connection fails 29 | ap: 30 | ssid: "${friendly_name} Fallback" 31 | password: "" # password of fallback hotspot 32 | 33 | # Enable Web Interface 34 | web_server: 35 | port: 80 36 | include_internal: true 37 | 38 | time: 39 | - platform: sntp 40 | id: sntp_time 41 | timezone: ${timezone} 42 | update_interval: 60min 43 | on_time_sync: 44 | then: 45 | - logger.log: --------- on_time_sync ---------" 46 | - globals.set: 47 | id: time_valid 48 | value: 'true' 49 | - lambda: 'MelachaPlug::runMelachaChecks();' 50 | on_time: 51 | # Run every minute 52 | - seconds: 0 53 | minutes: /1 54 | then: 55 | - logger.log: --------- on_time ---------" 56 | - lambda: 'MelachaPlug::runMelachaChecks();' 57 | 58 | sun: 59 | id: mysun 60 | latitude: ${latitude} 61 | longitude: ${longitude} 62 | 63 | # variables publicly (internally) available 64 | globals: 65 | - id: hdate_valid 66 | type: bool 67 | restore_value: no 68 | initial_value: "false" 69 | - id: time_valid 70 | type: bool 71 | restore_value: no 72 | initial_value: "false" 73 | 74 | switch: 75 | - platform: template 76 | name: 17 Eretz Yisrael 77 | id: eretz_yisrael 78 | optimistic: true # set to true for status to be updated immediately 79 | restore_mode: ${eretz_yisrael} 80 | on_turn_on: 81 | then: 82 | - logger.log: --------- on_turn_on eretz_yisrael ---------" 83 | - lambda: 'MelachaPlug::runMelachaChecks();' 84 | on_turn_off: 85 | then: 86 | - logger.log: --------- on_turn_off eretz_yisrael ---------" 87 | - lambda: 'MelachaPlug::runMelachaChecks();' 88 | - platform: template 89 | name: 24 Early Shabbos 90 | id: early_shabbos 91 | optimistic: true # set to true for status to be updated immediately 92 | restore_mode: RESTORE_DEFAULT_OFF 93 | on_turn_on: 94 | then: 95 | - logger.log: --------- on_turn_on early_shabbos ---------" 96 | - lambda: 'MelachaPlug::runMelachaChecks();' 97 | on_turn_off: 98 | then: 99 | - logger.log: --------- on_turn_off early_shabbos ---------" 100 | - lambda: 'MelachaPlug::runMelachaChecks();' 101 | - platform: template 102 | name: 18 Override Shabbos Mode 103 | id: override_shabbos_mode 104 | optimistic: true # set to true for status to be updated immediately 105 | restore_mode: RESTORE_DEFAULT_OFF 106 | - platform: template 107 | name: 25 Inverted Shabbos Mode 108 | id: inverted_shabbos_mode 109 | optimistic: true # set to true for status to be updated immediately 110 | restore_mode: ${inverted_shabbos_mode} 111 | on_turn_on: 112 | then: 113 | - logger.log: --------- on_turn_on inverted_shabbos_mode ---------" 114 | - lambda: 'MelachaPlug::runMelachaChecks();' 115 | on_turn_off: 116 | then: 117 | - logger.log: --------- on_turn_off inverted_shabbos_mode ---------" 118 | - lambda: 'MelachaPlug::runMelachaChecks();' 119 | 120 | # Shabbos mode switch - here is where we set what happens when it turns on and off 121 | - platform: template 122 | name: 19 Shabbos Mode 123 | id: shabbos_mode 124 | optimistic: true # set to true for status to be updated immediately 125 | turn_on_action: 126 | then: 127 | - if: 128 | condition: 129 | switch.is_on: inverted_shabbos_mode 130 | # lambda: 'return id(inverted_shabbos_mode).state == true;' 131 | then: 132 | - switch.turn_on: relay_1 # turn on plug for Shabbos and Yom Tov 133 | else: 134 | - switch.turn_off: relay_1 # turn off plug for Shabbos and Yom Tov 135 | - switch.turn_off: enable_button_1 # disables button for Shabbos and Yom Tov 136 | - switch.turn_on: flash_led 137 | turn_off_action: 138 | then: 139 | - switch.turn_off: flash_led # must be before relay 140 | - switch.turn_on: enable_button_1 # re-enable button for weekdays 141 | - if: 142 | condition: 143 | switch.is_on: inverted_shabbos_mode 144 | # lambda: 'return id(inverted_shabbos_mode).state == true;' 145 | then: 146 | - switch.turn_off: relay_1 # turn off plug for weekday 147 | else: 148 | - switch.turn_on: relay_1 # turn on plug for weekday 149 | 150 | text_sensor: 151 | - platform: template 152 | name: 01 Current Time 153 | internal: true 154 | lambda: 'return id(sntp_time).now().strftime("%a, %b %e, %Y, %l:%M %p");' 155 | update_interval: 60s 156 | - platform: template 157 | name: 02 Hebrew Date 158 | id: hebrew_date 159 | internal: true # Internal components will not be exposed to the frontend 160 | update_interval: never 161 | - platform: template 162 | name: 03 Todays Melacha Check Time 163 | id: todays_melacha_check_time 164 | internal: true # Internal components will not be exposed to the frontend 165 | update_interval: never 166 | - platform: sun 167 | name: 04 Next Sunset (elevated -0.833° - default) 168 | internal: true 169 | id: ts_sunset 170 | type: sunset 171 | format: "%a, %b %e, %l:%M %p" 172 | update_interval: never 173 | - platform: sun 174 | name: 05 Tzeit Hakochavim - Nightfall (elevated ${deg_tzeit}°) 175 | internal: true 176 | id: ts_tzeit 177 | type: sunset 178 | format: "%a, %b %e, %l:%M %p" 179 | update_interval: never 180 | elevation: ${deg_tzeit}° 181 | 182 | number: 183 | - platform: template 184 | name: 06 Minute Offset Start 185 | id: min_offset_start 186 | optimistic: true 187 | min_value: -300 188 | max_value: 120 189 | step: 0 190 | mode: box 191 | restore_value: true 192 | initial_value: ${min_offset_start} 193 | on_value: 194 | - logger.log: --------- on min_offset_start ---------" 195 | - lambda: 'MelachaPlug::runMelachaChecks();' 196 | - platform: template 197 | name: 07 Minute Offset End 198 | id: min_offset_end 199 | optimistic: true 200 | min_value: -120 201 | max_value: 120 202 | step: 0 203 | mode: box 204 | restore_value: true 205 | initial_value: ${min_offset_end} 206 | on_value: 207 | - logger.log: --------- on min_offset_end ---------" 208 | - lambda: 'MelachaPlug::runMelachaChecks();' 209 | - platform: template 210 | name: 08 Degree Shabbos Starts 211 | id: deg_shabbos_starts 212 | optimistic: true 213 | min_value: -5.000 214 | max_value: 30.000 # this allows for early shabbosim 215 | step: .001 216 | mode: box 217 | restore_value: true 218 | initial_value: ${deg_shabbos_starts} 219 | on_value: 220 | - logger.log: --------- on deg_shabbos_starts ---------" 221 | - lambda: 'MelachaPlug::runMelachaChecks();' 222 | - platform: template 223 | name: 09 Degree Shabbos Ends 224 | id: deg_shabbos_ends 225 | optimistic: true 226 | min_value: -5.000 227 | max_value: 30.000 228 | step: .001 229 | mode: box 230 | restore_value: true 231 | initial_value: ${deg_shabbos_ends} 232 | on_value: 233 | - logger.log: --------- on deg_shabbos_ends ---------" 234 | - lambda: 'MelachaPlug::runMelachaChecks();' 235 | 236 | button: 237 | - platform: template 238 | name: 10 Set To Alter Rebbe Zmanim 239 | on_press: 240 | - lambda: 'MelachaPlug::setAlterRebbeZmanim();' 241 | 242 | sensor: 243 | - platform: sun 244 | name: 11 Sun Elevation 245 | type: elevation 246 | internal: true 247 | -------------------------------------------------------------------------------- /esphome/plugins/melachaplug/optional_config.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #------------- Optional Config -------------- 3 | #-------------------------------------------- 4 | 5 | # Enable logger for debugging 6 | logger: 7 | 8 | # Enable Home Assistant API 9 | api: 10 | reboot_timeout: 0s # no need to connect to HA so no reboot necessary 11 | 12 | button: 13 | - platform: restart 14 | name: 26 Restart -------------------------------------------------------------------------------- /esphome/plugins/melachaplug/plug_info.yaml: -------------------------------------------------------------------------------- 1 | #-------------------------------------------- 2 | #---------------- Plug Info ----------------- 3 | #-------------------------------------------- 4 | switch: 5 | - platform: gpio 6 | name: 20 Relay 1 7 | id: relay_1 8 | pin: 9 | number: ${relay_1_gpio} 10 | restore_mode: ${relay_restore_mode} # defaults to on 11 | on_turn_on: 12 | then: 13 | - switch.turn_on: built_in_led 14 | on_turn_off: 15 | then: 16 | - switch.turn_off: built_in_led 17 | 18 | # switch which can disable the button 19 | - platform: gpio 20 | pin: 21 | number: ${button_1_gpio} 22 | allow_other_uses: true 23 | name: 21 Enable Button 1 24 | id: enable_button_1 25 | internal: true 26 | inverted: false 27 | restore_mode: ALWAYS_OFF # defaults to off in case of reset on Shabbos or Yom Tov 28 | 29 | # LED flash preset 30 | - platform: template 31 | name: 22 Flash LED 32 | id: flash_led 33 | disabled_by_default: true 34 | optimistic: yes 35 | restore_mode: DISABLED 36 | on_turn_on: 37 | - while: 38 | condition: 39 | switch.is_on: flash_led 40 | then: 41 | - switch.turn_on: built_in_led 42 | - delay: 75ms 43 | - switch.turn_off: built_in_led 44 | - delay: 75ms 45 | - switch.turn_on: built_in_led 46 | - delay: 75ms 47 | - switch.turn_off: built_in_led 48 | - delay: 2875ms 49 | on_turn_off: 50 | - switch.turn_off: built_in_led 51 | 52 | - platform: gpio 53 | name: 23 Built-in LED 54 | disabled_by_default: true 55 | id: built_in_led 56 | pin: 57 | number: ${built_in_led_gpio} 58 | restore_mode: ${relay_restore_mode} # defaults to the same as the relay 59 | inverted: ${led_inverted} 60 | 61 | binary_sensor: 62 | - platform: gpio 63 | id: button_1 64 | pin: 65 | number: ${button_1_gpio} 66 | mode: INPUT_PULLUP 67 | allow_other_uses: true 68 | inverted: true 69 | filters: 70 | - delayed_on: 10ms 71 | on_click: 72 | then: 73 | - switch.toggle: relay_1 74 | -------------------------------------------------------------------------------- /esphome/plugins/next_shabbbos_times/next_shabbbos_times.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include "../../../../plugins/hebrewcalender_melachaplug/hebrewcalender_melachaplug.h" 5 | 6 | #include "esphome.h" 7 | using namespace esphome; 8 | using namespace time; 9 | 10 | 11 | namespace NextShabbosTimes { 12 | void getSunsetFromDate(time::ESPTime &esp_time, float elevation) { 13 | ESP_LOGD("getSunsetFromDate", esp_time.strftime("Getting sunset of: %l:%M:%S %p, %A %D").c_str()); 14 | ESP_LOGD("getSunsetFromDate", "timestamp: %ld", esp_time.timestamp); 15 | esp_time.timestamp -= esp_time.hour * 60 * 60; // change the timestamp to 12am 16 | ESP_LOGD("getSunsetFromDate", "Updating timestamp to be 12am: %ld", esp_time.timestamp); 17 | id(mysun).set_time(esp_time); // temporarily change date of the sun 18 | esp_time = *id(mysun).sunset(elevation); // set esp_time to sunset time 19 | 20 | ESP_LOGD("getSunsetFromDate", esp_time.strftime("RESULT: %l:%M:%S %p, %A %D").c_str()); 21 | ESP_LOGD("getSunsetFromDate", "timestamp: %ld", esp_time.timestamp); 22 | id(mysun).set_time( id(sntp_time).now() ); // return sun to sntp time 23 | 24 | ESP_LOGD("getSunsetFromDate", "timestamp from the sun (should be back to cuurent date): %ld", id(mysun).sunset(elevation)->timestamp); 25 | } 26 | 27 | void getNextShabbosOnTime(time::ESPTime &esp_time, hdate &temp_hdate) { 28 | uint8_t counter = 0; 29 | while (isassurbemelachah(temp_hdate) == false) { 30 | // permitted to do melacha adding a day 31 | hdateaddday(&temp_hdate, 1); 32 | counter++; 33 | } 34 | while (counter > 1) { 35 | esp_time.increment_day(); 36 | counter--; 37 | } 38 | // publish shabbos on time 39 | NextShabbosTimes::getSunsetFromDate(esp_time, id(deg_shabbos_start_global)); 40 | id(ts_next_shabbos_on).publish_state(esp_time.strftime("%l:%M:%S %p, %A %D")); 41 | esp_time.increment_day(); // add a day to esptime to make it the same as hebrew date 42 | } 43 | void getNextShabbosOffTime(time::ESPTime &esp_time, hdate &temp_hdate) { 44 | uint8_t counter = 0; 45 | while (isassurbemelachah(temp_hdate) == true) { 46 | hdateaddday(&temp_hdate, 1); 47 | counter++; 48 | } 49 | while (counter > 1) { 50 | esp_time.increment_day(); 51 | counter--; 52 | } 53 | // publish shabbos off time 54 | NextShabbosTimes::getSunsetFromDate(esp_time, id(deg_shabbos_end_global)); 55 | id(ts_next_shabbos_off).publish_state(esp_time.strftime("%l:%M:%S %p, %A %D")); 56 | 57 | esp_time.increment_day(); // add a day to esptime to make it the same as hebrew date 58 | } 59 | void getNextShabbos() { 60 | bool published_off_time = false; 61 | time::ESPTime esp_time = id(sntp_time).now(); // create temp esptime 62 | hdate temp_hdate = convertDate(esp_time.to_c_tm()); // create hebrew date from esptime 63 | if (isassurbemelachah(temp_hdate) == true) { 64 | // if currently assur then move the temp day to the next day that's muttar 65 | NextShabbosTimes::getNextShabbosOffTime(esp_time, temp_hdate); 66 | published_off_time = true; // mark shabbos off time as published 67 | } 68 | NextShabbosTimes::getNextShabbosOnTime(esp_time, temp_hdate); 69 | if (published_off_time == false) { 70 | NextShabbosTimes::getNextShabbosOffTime(esp_time, temp_hdate); 71 | } 72 | } 73 | 74 | } // namespace NextShabbosTimes -------------------------------------------------------------------------------- /esphome/plugins/next_shabbbos_times/next_shabbbos_times.yaml: -------------------------------------------------------------------------------- 1 | esphome: 2 | includes: 3 | - plugins/next_shabbbos_times/next_shabbbos_times.h 4 | 5 | external_components: 6 | - source: github://pr#2933 7 | components: [ sun ] 8 | - source: github://pr#2955 9 | components: [ time ] 10 | 11 | text_sensor: 12 | - platform: template 13 | name: ${friendly_name} Next Shabbos Mode On 14 | internal: true 15 | id: ts_next_shabbos_on 16 | update_interval: never 17 | - platform: template 18 | name: ${friendly_name} Next Shabbos Mode Off 19 | internal: true 20 | id: ts_next_shabbos_off 21 | update_interval: never 22 | 23 | switch: 24 | - platform: template 25 | name: ${friendly_name} Get Next Shabbos 26 | disabled_by_default: true 27 | optimistic: yes 28 | turn_on_action: 29 | - lambda: 'NextShabbosTimes::getNextShabbos();' -------------------------------------------------------------------------------- /esphome/plugins/random_minute_generator/random_minute_generator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../../../plugins/hdate/hdate.h" 4 | 5 | #include "esphome.h" 6 | using namespace esphome; 7 | using namespace time; 8 | 9 | namespace RandomMinute { 10 | 11 | void generateRandomMinute() { 12 | // runs on every shabbos start time (and on boot) 13 | time_t timestamp_start = id(sntp_time).timestamp_now(); 14 | time_t timestamp_end = id(mysun).sunset( id(deg_shabbos_end_global) )->timestamp; 15 | 16 | // check if currently it's between Shabbos start and end elevation degree 17 | if (id(sntp_time).now().hour > 12 && id(mysun).elevation() > id(deg_shabbos_start_global) && id(mysun).elevation() < id(deg_shabbos_end_global)) { 18 | ESP_LOGD("RandomMinute::generateRandomMinute", "in between shabbos start-end elevation..adding a day"); 19 | timestamp_end = timestamp_end + 86400; // add a day 20 | } else { 21 | ESP_LOGD("RandomMinute::generateRandomMinute", "this should only run on boot (if shabbos mode is on)"); 22 | } 23 | ESP_LOGD("RandomMinute::generateRandomMinute", "timestamp_start: %ld timestamp_end: %ld", timestamp_start, timestamp_end); 24 | long int random_number = random(timestamp_start, timestamp_end); 25 | ESP_LOGD("RandomMinute::generateRandomMinute", "random_number: %ld", random_number); 26 | random_number -= random_number % 60; // rounds down minute to 0 seconds 27 | ESP_LOGD("RandomMinute::generateRandomMinute", "FIXED random_number: %ld", random_number); 28 | 29 | if (id(random_time_1) == 0){ 30 | id(random_time_1) = random_number; 31 | } else { 32 | id(random_time_2) = random_number; 33 | } 34 | } 35 | 36 | void relayMomentarilyTurnOff() { 37 | ESP_LOGD("RandomMinute::relayMomentarilyTurnOff", "Turning the relay momentarily off"); 38 | id(template_relay).turn_off(); 39 | } 40 | 41 | 42 | // ------------------------------------------------------------------------------------- 43 | void onBoot() { 44 | ESP_LOGD("RandomMinute::onBoot", "testing-------------"); 45 | // in case of reboot on Shabbos or Yom Tov 46 | if (isassurbemelachah(Hdate::current_hdate) == true) { 47 | RandomMinute::generateRandomMinute(); 48 | } 49 | } 50 | 51 | void onInterval() { 52 | if (id(hdate_valid) && id(shabbos_mode).state == true) { 53 | time_t timestamp_current = id(sntp_time).timestamp_now(); 54 | timestamp_current -= timestamp_current % 60; // rounds down minute to 0 seconds 55 | ESP_LOGD("RandomMinute::onInterval", "FIXED timestamp_current: %ld", timestamp_current); 56 | 57 | if (timestamp_current == id(random_time_1) ) { 58 | id(random_time_1) = 0; // reset time 59 | id(last_random).publish_state(to_string(timestamp_current)); 60 | RandomMinute::relayMomentarilyTurnOff(); 61 | } else if (timestamp_current == id(random_time_2) ) { 62 | id(random_time_2) = 0; // reset time 63 | id(last_random).publish_state(to_string(timestamp_current)); 64 | RandomMinute::relayMomentarilyTurnOff(); 65 | } 66 | } 67 | } 68 | 69 | void onShabbosStart() { 70 | ESP_LOGD("RandomMinute::onShabbosStart", "ran------"); 71 | if (isassurbemelachah(Hdate::current_hdate) == true) { 72 | // runs whether it's the first day or not 73 | ESP_LOGD("RandomMinute::onShabbosStart", "Generating random minute"); 74 | RandomMinute::generateRandomMinute(); 75 | } 76 | } 77 | } // namespace RandomMinute -------------------------------------------------------------------------------- /esphome/plugins/random_minute_generator/random_minute_generator.yaml: -------------------------------------------------------------------------------- 1 | esphome: 2 | includes: 3 | - plugins/random_minute_generator/random_minute_generator.h 4 | on_boot: 5 | - priority: -100.0 # pretty much everything should already be initialized 6 | then: 7 | - logger.log: "--- Waiting for hdate valid ---" 8 | - wait_until: 9 | lambda: 'return id(hdate_valid);' 10 | - logger.log: --------- hdate valid ---------" 11 | - lambda: 'RandomMinute::onBoot();' 12 | 13 | sun: 14 | on_sunset: 15 | - elevation: ${deg_shabbos_start}° # degree to assume Shabbos or Yom Tov starts 16 | then: 17 | - logger.log: "--- RandomMinute on sunset ---" 18 | - lambda: 'RandomMinute::onShabbosStart();' 19 | 20 | globals: 21 | - id: random_time_1 22 | type: long int 23 | restore_value: no 24 | initial_value: "0" 25 | - id: random_time_2 26 | type: long int 27 | restore_value: no 28 | initial_value: "0" 29 | 30 | text_sensor: 31 | - platform: template 32 | name: ${friendly_name} Last Random Shut Off 33 | internal: true 34 | id: last_random 35 | update_interval: never 36 | 37 | switch: 38 | - platform: template 39 | id: template_relay 40 | disabled_by_default: true 41 | optimistic: yes 42 | turn_off_action: 43 | - switch.turn_off: relay_1 44 | - delay: 60s 45 | - switch.turn_on: relay_1 46 | - switch.turn_on: template_relay 47 | - platform: template 48 | name: ${friendly_name} turn off relay 49 | disabled_by_default: true 50 | optimistic: yes 51 | turn_on_action: 52 | - lambda: 'id(template_relay).turn_off();' 53 | 54 | interval: 55 | - interval: 1min 56 | then: 57 | - lambda: 'RandomMinute::onInterval();' -------------------------------------------------------------------------------- /images/Melacha Plug Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chabad-source/melachaplug/9a3a00ae4f65cfa1f3cb6a996ffc9bd2418f0be0/images/Melacha Plug Banner.png -------------------------------------------------------------------------------- /images/Web Server Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chabad-source/melachaplug/9a3a00ae4f65cfa1f3cb6a996ffc9bd2418f0be0/images/Web Server Screenshot.png --------------------------------------------------------------------------------