From 370a6412a4816bd729eac9ea82ac6a66274a345f Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 22 Jun 2025 14:31:45 -0500 Subject: [PATCH] refactor: simple windows service demo, license --- .env.example | 1 - .gitignore | 5 +- .scripts/Install.ps1 | 31 -- LICENSE | 674 ++++++++++++++++++++++++++++++ README.md | 169 +++++++- cmd/windows/main.go | 77 ---- configs/hass_tray.service | 27 -- go.mod | 36 +- go.sum | 120 +----- go.work | 3 - go.work.sum | 5 - internal/app.go | 190 --------- internal/config.go | 16 - internal/go.mod | 26 -- internal/go.sum | 113 ----- internal/resources/closed.ico | Bin 7797 -> 0 bytes internal/resources/error.ico | Bin 7284 -> 0 bytes internal/resources/open.ico | Bin 7673 -> 0 bytes internal/resources/open_fault.ico | Bin 340 -> 0 bytes internal/resources/unknown.ico | Bin 7011 -> 0 bytes internal/types.go | 19 - internal/utility.go | 14 - main.go | 184 ++++++++ 23 files changed, 1019 insertions(+), 691 deletions(-) delete mode 100644 .env.example delete mode 100644 .scripts/Install.ps1 create mode 100644 LICENSE delete mode 100644 cmd/windows/main.go delete mode 100644 configs/hass_tray.service delete mode 100644 go.work delete mode 100644 go.work.sum delete mode 100644 internal/app.go delete mode 100644 internal/config.go delete mode 100644 internal/go.mod delete mode 100644 internal/go.sum delete mode 100644 internal/resources/closed.ico delete mode 100644 internal/resources/error.ico delete mode 100644 internal/resources/open.ico delete mode 100644 internal/resources/open_fault.ico delete mode 100644 internal/resources/unknown.ico delete mode 100644 internal/types.go delete mode 100644 internal/utility.go create mode 100644 main.go diff --git a/.env.example b/.env.example deleted file mode 100644 index 737cb23..0000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -HA_AUTH_TOKEN= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 80e98df..b9eb38e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ *.exe +*.log .env -.vscode \ No newline at end of file +.vscode +dist +.wix \ No newline at end of file diff --git a/.scripts/Install.ps1 b/.scripts/Install.ps1 deleted file mode 100644 index 6cd694d..0000000 --- a/.scripts/Install.ps1 +++ /dev/null @@ -1,31 +0,0 @@ -# A build & install script for the project's Windows version. -$ErrorActionPreference = "Stop" -$executableName = "door_tray" - -if (-Not (Test-Path -Path "./go.mod")) { - Write-Error "Please run this script from the project's root directory (go.mod not found)." - exit 1 -} - -# Build -go build -o "bin/$executableName-temp.exe" -ldflags "-s -w" "./cmd/windows/" -if ($LASTEXITCODE -ne 0) { - Write-Error "Build failed with exit code $LASTEXITCODE." - exit 1 -} - -# Compress -upx "bin/$executableName-temp.exe" -o "bin/$executableName.exe" -5 -f -if ($LASTEXITCODE -ne 0) { - Write-Error "Compression failed with exit code $LASTEXITCODE." - exit 1 -} -Remove-Item "bin/$executableName-temp.exe" - -# Setup service -$serviceName = "DoorTray" - -# TODO: Stop old service -# TODO: Install new binary -# TODO: Start & verify service -# TODO: Cleanup, print latest logs \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e72bfdd --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/README.md b/README.md index 4118c01..20d2b45 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,156 @@ -# door-tray +# HASS Tray - Windows Service -A simple Go application to display basic state details (via a tray icon) from a Home Assistant instance. +A minimal Go application that can run as a Windows service with JSON logging. -- Easy install with a single binary - - Windows (Service), Linux (Systemd) -- Ultra simple configuration - - YAML configuration file - - Environment variables - - Dotenv file -- Resistant to poor network conditions -- Lightweight, simple icons, simple UI options +## Features -## Wishlist +- Runs as a Windows service +- JSON logging with debug level +- Logs to `current.log` in the same directory as the executable +- Heartbeat logging every 5 seconds +- Interactive mode for testing -- [ ] Handle disconnections, reconnect automatically -- [ ] Event listening with watchdog -- [ ] Rotating System Logs -- [ ] Tray Icon Context Menu (Quit, Refresh, Open Log, Open Home Assistant) -- [ ] State Change Icons - - Icons that better show recent state changes (e.g. Orange for recently closed, Purple for bad connection) \ No newline at end of file +## Requirements + +- Go 1.21 or later +- Windows operating system +- Administrator privileges (for service installation) +- Task (taskfile.dev) - modern task runner + +## Installing Task + +Download and install Task from [taskfile.dev](https://taskfile.dev/installation/): + +```bash +# Using Chocolatey +choco install task + +# Using Scoop +scoop install task + +# Using winget +winget install Task.Task + +# Or download directly from GitHub releases +``` + +## Building + +```bash +task build +``` + +This will create `hass-tray.exe` in the current directory. + +## Installation + +```bash +task install +``` + +This will: +1. Build the application +2. Copy the executable to `%APPDATA%\hass-tray\` +3. Display instructions for installing as a Windows service + +To install as a Windows service (run as Administrator): + +```cmd +task service-install +``` + +Or manually: +```cmd +sc create hass-tray binPath= "%APPDATA%\hass-tray\hass-tray.exe" start= auto +sc description hass-tray "Home Assistant Tray Service" +sc start hass-tray +``` + +## Uninstallation + +```bash +task uninstall +``` + +This will: +1. Stop and remove the Windows service +2. Delete the executable from the AppData directory +3. Remove the installation directory + +## Testing + +To run the application in interactive mode for testing: + +```bash +task run +``` + +This will run the application directly and show debug output in the console. + +## Logging + +The application logs JSON-formatted messages to `current.log` in the same directory as the executable. Log entries include: + +- Timestamp in RFC3339 format +- Log level (debug) +- Message content +- Service name + +Example log entry: +```json +{"timestamp":"2024-01-15T10:30:00Z","level":"debug","message":"Service heartbeat","service":"hass-tray"} +``` + +## Service Management + +Once installed as a service, you can manage it using Task commands: + +- Install service: `task service-install` (requires admin) +- Uninstall service: `task service-uninstall` (requires admin) +- Start service: `task service-start` +- Stop service: `task service-stop` +- Check status: `task service-status` + +Or using standard Windows commands: + +- Start: `sc start hass-tray` +- Stop: `sc stop hass-tray` +- Status: `sc query hass-tray` +- Delete: `sc delete hass-tray` + +## Available Task Commands + +- `task build` - Build the application +- `task install` - Build and copy to AppData directory +- `task uninstall` - Remove service and files +- `task clean` - Remove build artifacts +- `task run` - Build and run in interactive mode +- `task test` - Run tests +- `task fmt` - Format Go code +- `task vet` - Vet Go code +- `task deps` - Download and tidy dependencies +- `task service-install` - Install as Windows service (requires admin) +- `task service-uninstall` - Uninstall Windows service (requires admin) +- `task service-start` - Start the service +- `task service-stop` - Stop the service +- `task service-status` - Check service status +- `task dev` - Complete development workflow +- `task` - Show all available tasks + +## Development + +The application uses the `golang.org/x/sys/windows/svc` package for Windows service functionality. The main service logic is in the `Execute` method of the `myservice` struct. + +When running in interactive mode (not as a service), the application will run the same logic but in the foreground with console output. + +## Why Task instead of Make? + +Task provides several advantages over Make: + +- **Better Windows support** - Native Windows compatibility +- **YAML configuration** - More readable and maintainable +- **Cross-platform** - Works on Windows, macOS, and Linux +- **Dependencies** - Better task dependency management +- **Variables** - More flexible variable handling +- **Parallel execution** - Built-in support for parallel task execution +- **Silent mode** - Better control over output verbosity \ No newline at end of file diff --git a/cmd/windows/main.go b/cmd/windows/main.go deleted file mode 100644 index 50d46e0..0000000 --- a/cmd/windows/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "log" - "log/slog" - "time" - - "internal" - - "golang.org/x/sys/windows/svc" - "golang.org/x/sys/windows/svc/debug" -) - -type WrapperService struct { - service internal.Service -} - -func (wrapper *WrapperService) Execute(args []string, requestChannel <-chan svc.ChangeRequest, status chan<- svc.Status) (bool, uint32) { - const acceptedCommands = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue - tick := time.Tick(5 * time.Second) - - status <- svc.Status{State: svc.StartPending} - status <- svc.Status{State: svc.Running, Accepts: acceptedCommands} - -loop: - for { - select { - case <-tick: - slog.Debug("Tick Handled...!") - case changeRequest := <-requestChannel: - switch changeRequest.Cmd { - case svc.Interrogate: - slog.Debug("Interrogate Requested", "changeRequest", changeRequest) - status <- changeRequest.CurrentStatus - case svc.Stop, svc.Shutdown: - slog.Warn("Shutdown Requested", "changeRequest", changeRequest) - break loop - case svc.Pause: - wrapper.service.Pause() - slog.Warn("Pause Requested", "changeRequest", changeRequest) - status <- svc.Status{State: svc.Paused, Accepts: acceptedCommands} - case svc.Continue: - slog.Info("Continue Requested", "changeRequest", changeRequest) - status <- svc.Status{State: svc.Running, Accepts: acceptedCommands} - default: - slog.Warn("Unexpected Change Request", "changeRequest", changeRequest) - } - } - } - - slog.Info("Service Stopping") - status <- svc.Status{State: svc.StopPending} - return false, 1 -} - -func runService(name string, isDebug bool) { - service := WrapperService{ - service: internal.NewApp(), - } - - if isDebug { - err := debug.Run(name, &service) - if err != nil { - log.Fatalln("Error running service in debug mode.") - } - } else { - err := svc.Run(name, &service) - if err != nil { - log.Fatalln("Error running service in Service Control mode.") - } - } -} - -func main() { - - runService("DoorTray", true) -} diff --git a/configs/hass_tray.service b/configs/hass_tray.service deleted file mode 100644 index af33905..0000000 --- a/configs/hass_tray.service +++ /dev/null @@ -1,27 +0,0 @@ -[Unit] -Description=A description -ConditionPathExists=/home/ubuntu/work/src/door_tray/door_tray -After=network.target - -[Service] -Type=simple -User=door_tray -Group=door_tray -LimitNOFILE=1024 - -Restart=on-failure -RestartSec=10 -startLimitIntervalSec=60 - -WorkingDirectory=/home/ubuntu/work/src/door_tray -ExecStart=/home/ubuntu/work/src/door_tray/door_tray --name=foo - -# make sure log directory exists and owned by syslog -PermissionsStartOnly=true -ExecStartPre=/bin/mkdir -p /var/log/door_tray -ExecStartPre=/bin/chown syslog:adm /var/log/door_tray -ExecStartPre=/bin/chmod 755 /var/log/door_tray -SyslogIdentifier=door_tray - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/go.mod b/go.mod index d1b8428..9952a93 100644 --- a/go.mod +++ b/go.mod @@ -1,35 +1,5 @@ -module xevion.dev/door-tray +module hass-tray -go 1.23.1 +go 1.21 -require ( - golang.org/x/sys v0.1.0 - internal v1.0.0 -) - -replace internal => ./internal/ - -require ( - github.com/getlantern/systray v1.2.2 // indirect - github.com/joho/godotenv v1.5.1 // indirect - golang.org/x/text v0.19.0 // indirect - saml.dev/gome-assistant v0.2.3 // indirect -) - -require ( - github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect - github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect - github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect - github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect - github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect - github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/gobuffalo/envy v1.10.2 // indirect - github.com/gobuffalo/packd v1.0.2 // indirect - github.com/gobuffalo/packr v1.30.1 // indirect - github.com/golang-module/carbon v1.7.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/nathan-osman/go-sunrise v1.1.0 // indirect - github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect -) +require golang.org/x/sys v0.15.0 diff --git a/go.sum b/go.sum index 8df370d..063d2d3 100644 --- a/go.sum +++ b/go.sum @@ -1,118 +1,2 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= -github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= -github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So= -github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= -github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk= -github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc= -github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= -github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= -github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= -github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= -github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= -github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= -github.com/getlantern/systray v1.2.2 h1:dCEHtfmvkJG7HZ8lS/sLklTH4RKUcIsKrAD9sThoEBE= -github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgNMY5HvhHhcE= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= -github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= -github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= -github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= -github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= -github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= -github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= -github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= -github.com/golang-module/carbon v1.7.1 h1:EDPV0YjxeS2kE2cRedfGgDikU6l5D79HB/teHuZDLu8= -github.com/golang-module/carbon v1.7.1/go.mod h1:M/TDTYPp3qWtW68u49dLDJOyGmls6L6BXdo/pyvkMaU= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= -github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/nathan-osman/go-sunrise v1.1.0 h1:ZqZmtmtzs8Os/DGQYi0YMHpuUqR/iRoJK+wDO0wTCw8= -github.com/nathan-osman/go-sunrise v1.1.0/go.mod h1:RcWqhT+5ShCZDev79GuWLayetpJp78RSjSWxiDowmlM= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -saml.dev/gome-assistant v0.2.3 h1:WuT0yMuUjyM78eHYjry/mbhMypSZ7GKmc4vr+Y4YOQ4= -saml.dev/gome-assistant v0.2.3/go.mod h1:NAj56yKBq4PXmHIdrn9oeTmM5SHaNYoLfSWpzTpKjXY= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/go.work b/go.work deleted file mode 100644 index e2f8485..0000000 --- a/go.work +++ /dev/null @@ -1,3 +0,0 @@ -go 1.23.1 - -use ./internal diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index 92c146f..0000000 --- a/go.work.sum +++ /dev/null @@ -1,5 +0,0 @@ -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= diff --git a/internal/app.go b/internal/app.go deleted file mode 100644 index 7702ae7..0000000 --- a/internal/app.go +++ /dev/null @@ -1,190 +0,0 @@ -package internal - -import ( - "embed" - "fmt" - "log/slog" - "os" - "os/signal" - "runtime" - "time" - - "github.com/getlantern/systray" - dotenv "github.com/joho/godotenv" - "golang.org/x/text/cases" - "golang.org/x/text/language" - ga "saml.dev/gome-assistant" -) - -var () - -type TrayApp struct { - doorIdentifier string - log *slog.Logger - stateChannel chan string - app *ga.App - service *ga.Service -} - -// Status will return the operational status of the service -func (ta *TrayApp) Status() Status { - return StatusUnknown -} - -func (ta *TrayApp) State() string { - // TODO: Implement this method - return "" -} - -func (ta *TrayApp) Connected() bool { - // TODO: Implement this method - return false -} - -func (ta *TrayApp) Reload() error { - // TODO: Implement this method - return nil -} - -func (ta *TrayApp) Pause() error { - // TODO: Implement this method - return nil -} - -func (ta *TrayApp) Resume() error { - // TODO: Implement this method - return nil -} - -func NewApp() *TrayApp { - // Connect to Home Assistant - app, err := ga.NewApp(ga.NewAppRequest{ - IpAddress: "home.imfucked.lol", // Replace with your Home Assistant IP Address - HAAuthToken: os.Getenv("HA_AUTH_TOKEN"), - HomeZoneEntityId: "zone.home", - Port: "443", - Secure: true, - }) - if err != nil { - slog.Error("Error connecting to Home Assistant", "error", err) - os.Exit(1) - } - - service := app.GetService() - - return &TrayApp{ - app: app, - service: service, - stateChannel: make(chan string), - doorIdentifier: "binary_sensor.bedroom_door_opening", - } -} - -var ( - //go:embed "resources/*.ico" - icons embed.FS -) - -func (ta *TrayApp) HandleState(newState string) { - switch newState { - case "on": - ta.stateChannel <- "open" - case "off": - ta.stateChannel <- "closed" - default: - slog.Error("unknown state encountered", "newState", newState) - ta.stateChannel <- "unknown" - } -} - -func (ta *TrayApp) setupHomeAssistant() { - var err error - - // Get the initial state - state, err := ta.app.GetState().Get(ta.doorIdentifier) - if err != nil { - slog.Error("Unable to get initial state", "error", err) - } else { - slog.Debug("Initial State Received") - ta.HandleState(state.State) - } - - ta.app.RegisterEntityListeners(ga. - NewEntityListener(). - EntityIds(ta.doorIdentifier). - Call(func(service *ga.Service, state ga.State, sensor ga.EntityData) { - slog.Debug("Event Received", "identifier", ta.doorIdentifier, "sensor", sensor) - ta.HandleState(sensor.ToState) - }). - Build()) - - ta.app.Start() - - slog.Warn("Home Assistant thread died") - ta.stateChannel <- "unknown" -} - -func (ta *TrayApp) Start() { - dotenv.Load() - - slog.SetDefault(slog.New(slog.NewJSONHandler( - os.Stdout, - &slog.HandlerOptions{ - Level: slog.LevelDebug, - }, - ))) - // binfo, err := buildinfo. - slog.Info("Startup", "runtime", runtime.Version(), "os", runtime.GOOS, "arch", runtime.GOARCH, "pid", os.Getpid()) - - go ta.setupHomeAssistant() - systray.Run(ta.onReady, func() {}) -} - -func (ta *TrayApp) onReady() { - systray.SetTitle("door-tray") - systray.SetTooltip("Setting up...") - menuQuit := systray.AddMenuItem("Quit", "Stops the application") - menuOpenLogs := systray.AddMenuItem("Open Logs", "Opens the logs in the default editor") - menuOpenLogs.Disable() - - // Load icons - systray.SetIcon(getIcon("unknown")) - - // Handle Ctrl+C interrupt - interruptChannel := make(chan os.Signal, 1) - signal.Notify(interruptChannel, os.Interrupt) - signal.Notify(interruptChannel, os.Kill) - -loop: - for { - select { - case signal := <-interruptChannel: - slog.Info("Received interrupt signal, quitting", "signal", signal) - break loop - case <-menuQuit.ClickedCh: - slog.Info("Quit clicked") - break loop - case <-menuOpenLogs.ClickedCh: - slog.Info("Open Logs clicked") - case newState := <-ta.stateChannel: - timeString := time.Now().Format("3:04 PM") - if newState != "unknown" { - systray.SetTooltip(fmt.Sprintf("%s as of %s", cases.Title(language.AmericanEnglish, cases.NoLower).String(newState), timeString)) - switch newState { - case "open": - systray.SetIcon(getIcon("open_fault")) - case "closed": - systray.SetIcon(getIcon("closed")) - } - } else { - slog.Warn("Unknown state", "state", newState) - systray.SetTooltip(fmt.Sprintf("Unknown as of %s", timeString)) - systray.SetIcon(getIcon("unknown")) - } - } - } - - slog.Info("Cleaning up") - systray.Quit() - ta.app.Cleanup() -} diff --git a/internal/config.go b/internal/config.go deleted file mode 100644 index a207a21..0000000 --- a/internal/config.go +++ /dev/null @@ -1,16 +0,0 @@ -package internal - -import ( - "fmt" - "log/slog" - "os" -) - -func getIcon(icon string) []byte { - iconBytes, err := icons.ReadFile(fmt.Sprintf("resources/%s.ico", icon)) - if err != nil { - slog.Error("Unable to load icon", "error", err) - os.Exit(1) - } - return iconBytes -} diff --git a/internal/go.mod b/internal/go.mod deleted file mode 100644 index 4630e8c..0000000 --- a/internal/go.mod +++ /dev/null @@ -1,26 +0,0 @@ -module internal - -go 1.22.0 - -require ( - github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 // indirect - github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 // indirect - github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 // indirect - github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect - github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect - github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect - github.com/getlantern/systray v1.2.2 // indirect - github.com/go-stack/stack v1.8.0 // indirect - github.com/gobuffalo/envy v1.10.2 // indirect - github.com/gobuffalo/packd v1.0.2 // indirect - github.com/gobuffalo/packr v1.30.1 // indirect - github.com/golang-module/carbon v1.7.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/joho/godotenv v1.5.1 // indirect - github.com/nathan-osman/go-sunrise v1.1.0 // indirect - github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.19.0 // indirect - saml.dev/gome-assistant v0.2.3 // indirect -) diff --git a/internal/go.sum b/internal/go.sum deleted file mode 100644 index bb92348..0000000 --- a/internal/go.sum +++ /dev/null @@ -1,113 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/getlantern/context v0.0.0-20190109183933-c447772a6520 h1:NRUJuo3v3WGC/g5YiyF790gut6oQr5f3FBI88Wv0dx4= -github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= -github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7 h1:6uJ+sZ/e03gkbqZ0kUG6mfKoqDb4XMAzMIwlajq19So= -github.com/getlantern/errors v0.0.0-20190325191628-abdb3e3e36f7/go.mod h1:l+xpFBrCtDLpK9qNjxs+cHU6+BAdlBaxHqikB6Lku3A= -github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7 h1:guBYzEaLz0Vfc/jv0czrr2z7qyzTOGC9hiQ0VC+hKjk= -github.com/getlantern/golog v0.0.0-20190830074920-4ef2e798c2d7/go.mod h1:zx/1xUUeYPy3Pcmet8OSXLbF47l+3y6hIPpyLWoR9oc= -github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 h1:micT5vkcr9tOVk1FiH8SWKID8ultN44Z+yzd2y/Vyb0= -github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7/go.mod h1:dD3CgOrwlzca8ed61CsZouQS5h5jIzkK9ZWrTcf0s+o= -github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 h1:XYzSdCbkzOC0FDNrgJqGRo8PCMFOBFL9py72DRs7bmc= -github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55/go.mod h1:6mmzY2kW1TOOrVy+r41Za2MxXM+hhqTtY3oBKd2AgFA= -github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f h1:wrYrQttPS8FHIRSlsrcuKazukx/xqO/PpLZzZXsF+EA= -github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f/go.mod h1:D5ao98qkA6pxftxoqzibIBBrLSUli+kYnJqrgBf9cIA= -github.com/getlantern/systray v1.2.2 h1:dCEHtfmvkJG7HZ8lS/sLklTH4RKUcIsKrAD9sThoEBE= -github.com/getlantern/systray v1.2.2/go.mod h1:pXFOI1wwqwYXEhLPm9ZGjS2u/vVELeIgNMY5HvhHhcE= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.10.2 h1:EIi03p9c3yeuRCFPOKcSfajzkLb3hrRjEpHGI8I2Wo4= -github.com/gobuffalo/envy v1.10.2/go.mod h1:qGAGwdvDsaEtPhfBzb3o0SfDea8ByGn9j8bKmVft9z8= -github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= -github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= -github.com/gobuffalo/packd v1.0.2 h1:Yg523YqnOxGIWCp69W12yYBKsoChwI7mtu6ceM9Bwfw= -github.com/gobuffalo/packd v1.0.2/go.mod h1:sUc61tDqGMXON80zpKGp92lDb86Km28jfvX7IAyxFT8= -github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= -github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= -github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= -github.com/golang-module/carbon v1.7.1 h1:EDPV0YjxeS2kE2cRedfGgDikU6l5D79HB/teHuZDLu8= -github.com/golang-module/carbon v1.7.1/go.mod h1:M/TDTYPp3qWtW68u49dLDJOyGmls6L6BXdo/pyvkMaU= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= -github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/nathan-osman/go-sunrise v1.1.0 h1:ZqZmtmtzs8Os/DGQYi0YMHpuUqR/iRoJK+wDO0wTCw8= -github.com/nathan-osman/go-sunrise v1.1.0/go.mod h1:RcWqhT+5ShCZDev79GuWLayetpJp78RSjSWxiDowmlM= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= -github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -saml.dev/gome-assistant v0.2.3 h1:WuT0yMuUjyM78eHYjry/mbhMypSZ7GKmc4vr+Y4YOQ4= -saml.dev/gome-assistant v0.2.3/go.mod h1:NAj56yKBq4PXmHIdrn9oeTmM5SHaNYoLfSWpzTpKjXY= diff --git a/internal/resources/closed.ico b/internal/resources/closed.ico deleted file mode 100644 index ecf7c992477909a0c38852297854298881056cc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7797 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 diff --git a/internal/resources/error.ico b/internal/resources/error.ico deleted file mode 100644 index 5ada5170e30c7d35a67f5ed13946ad2ccd0eec83..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7284 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 diff --git a/internal/resources/open.ico b/internal/resources/open.ico deleted file mode 100644 index 618f697dd7c960542ddb542f416c33cbe482e8b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7673 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 diff --git a/internal/resources/open_fault.ico b/internal/resources/open_fault.ico deleted file mode 100644 index d7dec933509d484c25bdd348072758b0291e101d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 340 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 diff --git a/internal/types.go b/internal/types.go deleted file mode 100644 index 0191cf6..0000000 --- a/internal/types.go +++ /dev/null @@ -1,19 +0,0 @@ -package internal - -type Status int - -const ( - StatusUnknown Status = iota - StatusRunning - StatusStopped - StatusError -) - -type Service interface { - Status() Status - State() string - Connected() bool - Reload() error - Pause() error - Resume() error -} diff --git a/internal/utility.go b/internal/utility.go deleted file mode 100644 index 87a7008..0000000 --- a/internal/utility.go +++ /dev/null @@ -1,14 +0,0 @@ -package internal - -import "runtime/debug" - -var Commit = func() string { - if info, ok := debug.ReadBuildInfo(); ok { - for _, setting := range info.Settings { - if setting.Key == "vcs.revision" { - return setting.Value - } - } - } - return "" -}() diff --git a/main.go b/main.go new file mode 100644 index 0000000..8787062 --- /dev/null +++ b/main.go @@ -0,0 +1,184 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + "time" + + "golang.org/x/sys/windows/svc" + "golang.org/x/sys/windows/svc/debug" + "golang.org/x/sys/windows/svc/eventlog" +) + +var elog debug.Log + +type myservice struct{} + +func (m *myservice) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) { + const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown | svc.AcceptPauseAndContinue + changes <- svc.Status{State: svc.StartPending} + + // Get the directory where the executable is located + exePath, err := os.Executable() + if err != nil { + elog.Error(1, fmt.Sprintf("Failed to get executable path: %v", err)) + return + } + exeDir := filepath.Dir(exePath) + + // Open log file in the same directory as the executable + logFile, err := os.OpenFile(filepath.Join(exeDir, "current.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + elog.Error(1, fmt.Sprintf("Failed to open log file: %v", err)) + return + } + defer logFile.Close() + + // Create JSON logger + logger := log.New(logFile, "", 0) + + // Log startup + startupLog := map[string]interface{}{ + "timestamp": time.Now().Format(time.RFC3339), + "level": "debug", + "message": "Service starting", + "service": "hass-tray", + } + startupJSON, _ := json.Marshal(startupLog) + logger.Println(string(startupJSON)) + + changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} + + // Main service loop + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + for { + select { + case c := <-r: + switch c.Cmd { + case svc.Interrogate: + changes <- c.CurrentStatus + case svc.Stop, svc.Shutdown: + // Log shutdown + shutdownLog := map[string]interface{}{ + "timestamp": time.Now().Format(time.RFC3339), + "level": "debug", + "message": "Service stopping", + "service": "hass-tray", + } + shutdownJSON, _ := json.Marshal(shutdownLog) + logger.Println(string(shutdownJSON)) + + changes <- svc.Status{State: svc.StopPending} + return + case svc.Pause: + changes <- svc.Status{State: svc.Paused, Accepts: cmdsAccepted} + case svc.Continue: + changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted} + default: + elog.Error(uint32(1), fmt.Sprintf("unexpected control request #%d", c)) + } + case <-ticker.C: + // Log heartbeat + heartbeatLog := map[string]interface{}{ + "timestamp": time.Now().Format(time.RFC3339), + "level": "debug", + "message": "Service heartbeat", + "service": "hass-tray", + } + heartbeatJSON, _ := json.Marshal(heartbeatLog) + logger.Println(string(heartbeatJSON)) + } + } +} + +func runService(name string, isDebug bool) { + var err error + if isDebug { + elog = debug.New(name) + } else { + elog, err = eventlog.Open(name) + if err != nil { + return + } + } + defer elog.Close() + + elog.Info(1, fmt.Sprintf("starting %s service", name)) + run := svc.Run + if isDebug { + run = debug.Run + } + err = run(name, &myservice{}) + if err != nil { + elog.Error(1, fmt.Sprintf("%s service failed: %v", name, err)) + return + } + elog.Info(1, fmt.Sprintf("%s service stopped", name)) +} + +func main() { + isDebug, err := svc.IsAnInteractiveSession() + if err != nil { + log.Fatalf("failed to determine if we are running in an interactive session: %v", err) + } + + if !isDebug { + runService("hass-tray", false) + return + } + + // Interactive mode - just run the service logic directly + fmt.Println("Running in interactive mode...") + + // Get the current directory for log file + currentDir, err := os.Getwd() + if err != nil { + log.Fatalf("Failed to get current directory: %v", err) + } + + // Open log file + logFile, err := os.OpenFile(filepath.Join(currentDir, "current.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatalf("Failed to open log file: %v", err) + } + defer logFile.Close() + + // Create JSON logger + logger := log.New(logFile, "", 0) + + // Log startup + startupLog := map[string]interface{}{ + "timestamp": time.Now().Format(time.RFC3339), + "level": "debug", + "message": "Application starting in interactive mode", + "service": "hass-tray", + } + startupJSON, _ := json.Marshal(startupLog) + logger.Println(string(startupJSON)) + + fmt.Println("Press Ctrl+C to stop...") + + // Simple interactive loop + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + // Log heartbeat + heartbeatLog := map[string]interface{}{ + "timestamp": time.Now().Format(time.RFC3339), + "level": "debug", + "message": "Application heartbeat", + "service": "hass-tray", + } + heartbeatJSON, _ := json.Marshal(heartbeatLog) + logger.Println(string(heartbeatJSON)) + } + } +}