├── .github ├── FUNDING.yml └── workflows │ ├── automerge.yaml │ ├── hacs.yaml │ ├── hassfest.yaml │ └── release.yaml ├── LICENSE ├── README.md ├── custom_components └── saver │ ├── __init__.py │ ├── config_flow.py │ ├── const.py │ ├── manifest.json │ ├── services.yaml │ ├── strings.json │ └── translations │ ├── en.json │ └── pl.json └── hacs.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: piotrmachowski 2 | custom: ["buycoffee.to/piotrmachowski", "paypal.me/PiMachowski", "revolut.me/314ma"] 3 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yaml: -------------------------------------------------------------------------------- 1 | name: 'Automatically merge master -> dev' 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | name: Automatically merge master to dev 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | name: Git checkout 15 | with: 16 | fetch-depth: 0 17 | - name: Merge master -> dev 18 | run: | 19 | git config user.name "GitHub Actions" 20 | git config user.email "PiotrMachowski@users.noreply.github.com" 21 | if (git checkout dev) 22 | then 23 | git merge --ff-only master || git merge --no-commit master 24 | git commit -m "Automatically merge master -> dev" || echo "No commit needed" 25 | git push origin dev 26 | else 27 | echo "No dev branch" 28 | fi -------------------------------------------------------------------------------- /.github/workflows/hacs.yaml: -------------------------------------------------------------------------------- 1 | name: Validate HACS 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | name: Download repo 11 | with: 12 | fetch-depth: 0 13 | 14 | - uses: actions/setup-python@v2 15 | name: Setup Python 16 | with: 17 | python-version: '3.8.x' 18 | 19 | - uses: actions/cache@v2 20 | name: Cache 21 | with: 22 | path: | 23 | ~/.cache/pip 24 | key: custom-component-ci 25 | 26 | - name: HACS Action 27 | uses: hacs/action@main 28 | with: 29 | CATEGORY: integration -------------------------------------------------------------------------------- /.github/workflows/hassfest.yaml: -------------------------------------------------------------------------------- 1 | name: Validate with hassfest 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | validate: 9 | runs-on: "ubuntu-latest" 10 | steps: 11 | - uses: "actions/checkout@v2" 12 | - uses: home-assistant/actions/hassfest@master 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | release: 9 | name: Prepare release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Download repo 13 | uses: actions/checkout@v1 14 | 15 | - name: Zip saver dir 16 | run: | 17 | cd /home/runner/work/Home-Assistant-custom-components-Saver/Home-Assistant-custom-components-Saver/custom_components/saver 18 | zip saver.zip -r ./ 19 | 20 | - name: Upload zip to release 21 | uses: svenstaro/upload-release-action@v1-release 22 | with: 23 | repo_token: ${{ secrets.GITHUB_TOKEN }} 24 | file: /home/runner/work/Home-Assistant-custom-components-Saver/Home-Assistant-custom-components-Saver/custom_components/saver/saver.zip 25 | asset_name: saver.zip 26 | tag: ${{ github.ref }} 27 | overwrite: true -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Piotr Machowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![HACS Default][hacs_shield]][hacs] 2 | [![GitHub Latest Release][releases_shield]][latest_release] 3 | [![GitHub All Releases][downloads_total_shield]][releases] 4 | [![Community Forum][community_forum_shield]][community_forum] 5 | [![Ko-Fi][ko_fi_shield]][ko_fi] 6 | [![buycoffee.to][buycoffee_to_shield]][buycoffee_to] 7 | [![PayPal.Me][paypal_me_shield]][paypal_me] 8 | [![Revolut.Me][revolut_me_shield]][revolut_me] 9 | 10 | 11 | 12 | [hacs_shield]: https://img.shields.io/static/v1.svg?label=HACS&message=Default&style=popout&color=green&labelColor=41bdf5&logo=HomeAssistantCommunityStore&logoColor=white 13 | [hacs]: https://hacs.xyz/docs/default_repositories 14 | 15 | [latest_release]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-Saver/releases/latest 16 | [releases_shield]: https://img.shields.io/github/release/PiotrMachowski/Home-Assistant-custom-components-Saver.svg?style=popout 17 | 18 | [releases]: https://github.com/PiotrMachowski/Home-Assistant-custom-components-Saver/releases 19 | [downloads_total_shield]: https://img.shields.io/github/downloads/PiotrMachowski/Home-Assistant-custom-components-Saver/total 20 | 21 | [community_forum_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Forum&style=popout&color=41bdf5&logo=HomeAssistant&logoColor=white 22 | [community_forum]: https://community.home-assistant.io/t/custom-component-saver/204249 23 | 24 | 25 | # Saver 26 | 27 | This custom component allows you to save current state of any entity and use its data later to restore it. 28 | 29 | Additionally, you can create simple variables and use their values in scripts. 30 | 31 | ## Installation 32 | 33 | ### Using [HACS](https://hacs.xyz/) (recommended) 34 | 35 | [![Open your Home Assistant instance and open a repository inside the Home Assistant Community Store.](https://my.home-assistant.io/badges/hacs_repository.svg)](https://my.home-assistant.io/redirect/hacs_repository/?owner=PiotrMachowski&repository=Home-Assistant-custom-components-Saver&category=Integration) 36 | 37 | This integration can be installed using HACS. 38 | To do it search for `Saver` in *Integrations* section. 39 | 40 | ### Manual 41 | 42 | To install this integration manually you have to download [*saver.zip*](https://github.com/PiotrMachowski/Home-Assistant-custom-components-Saver/releases/latest/download/saver.zip) and extract its contents to `config/custom_components/saver` directory: 43 | ```bash 44 | mkdir -p custom_components/saver 45 | cd custom_components/saver 46 | wget https://github.com/PiotrMachowski/Home-Assistant-custom-components-Saver/releases/latest/download/saver.zip 47 | unzip saver.zip 48 | rm saver.zip 49 | ``` 50 | 51 | ## Configuration 52 | 53 | ### Using UI 54 | 55 | [![Open your Home Assistant instance and start setting up a new integration.](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=saver) 56 | 57 | From the Home Assistant front page go to **Configuration** and then select **Integrations** from the list. 58 | 59 | Use the "plus" button in the bottom right to add a new integration called **Saver**. 60 | 61 | The success dialog will appear or an error will be displayed in the popup. 62 | 63 | ### YAML 64 | 65 | Add following code in configuration.yaml: 66 | ```yaml 67 | saver: 68 | ``` 69 | 70 | ## Available services 71 | 72 | ### Save state 73 | Saves the state and parameters of the entity. 74 | ```yaml 75 | service: saver.save_state 76 | data: 77 | entity_id: cover.living_room 78 | ``` 79 | 80 | ### Delete saved state 81 | Deletes a saved state for an entity. 82 | ```yaml 83 | service: saver.delete 84 | data: 85 | entity_id: cover.living_room 86 | ``` 87 | 88 | ### Set variable 89 | Sets the value to the variable. 90 | ```yaml 91 | service: saver.set_variable 92 | data: 93 | name: counter 94 | value: 3 95 | ``` 96 | 97 | ### Delete variable 98 | Deletes a saved variable. 99 | ```yaml 100 | service: saver.delete_variable 101 | data: 102 | name: counter 103 | ``` 104 | 105 | ### Clear 106 | Deletes all saved data. 107 | ```yaml 108 | service: saver.clear 109 | ``` 110 | 111 | ### Deprecated services 112 | 113 |
114 | Show 115 | 116 | 117 | #### Restore state 118 | Executes the script using saved state of the entity. 119 | 120 | To use state of an entity you have to use a following value in a template: `state`. 121 | 122 | To use state attribute (in this case `current_position`) of an entity you have to use a following value in a template: `attr_current_position`. 123 | 124 | ```yaml 125 | service: saver.restore_state 126 | data: 127 | entity_id: cover.living_room 128 | restore_script: 129 | - service: cover.set_cover_position 130 | data_template: 131 | entity_id: cover.living_room 132 | position: "{{ '{{ attr_current_position | int }}' }}" 133 | - service: input_text.set_value 134 | data_template: 135 | entity_id: input_text.cover_description 136 | value: "Cover is now {{ '{{ state }}' }}" 137 | ``` 138 | 139 | #### Execute script 140 | Executes a script using all saved entities and variables. 141 | 142 | To use state of an entity (in this case `cover.living_room`) you have to use a following value in a template: `cover_living_room_state`. 143 | 144 | To use state attribute (in this case `current_position`) of an entity you have to use a following value in a template: `cover_living_room_attr_current_position`. 145 | 146 | ```yaml 147 | service: saver.execute 148 | data: 149 | script: 150 | - service: cover.set_cover_position 151 | data_template: 152 | entity_id: cover.living_room 153 | position: "{{ '{{ cover_living_room_attr_current_position | int }}' }}" 154 | - service: input_text.set_value 155 | data_template: 156 | entity_id: input_text.cover_description 157 | value: "Cover is now {{ '{{ cover_living_room_state }}' }}" 158 | - service: input_text.set_value 159 | data_template: 160 | entity_id: input_text.counter_description 161 | value: "Counter has value {{ '{{ counter }}' }}" 162 | ``` 163 | 164 | 165 |
166 | 167 | 168 | ## Using saved values in templates 169 | It is possible to use saved data in templates using `saver_entity` and `saver_variable` functions: 170 | ```yaml 171 | {{ saver_entity("sun.sun") }} # returns saved state of "sun.sun" entity 172 | {{ saver_entity("sun.sun", "azimuth") }} # returns "azimuth" attribute of saved "sun.sun" entity 173 | {{ saver_variable("counter") }} # returns saved variable "counter" 174 | ``` 175 | 176 | ## Events 177 | 178 | After the completion of the services mentioned before, the following events are fired: 179 | 180 | | **Service Function** | **Event ID** | **Provided Arguments** | 181 | |----------------------|------------------------------|------------------------| 182 | | **execute** | event_saver_executed | script | 183 | | **save_state** | event_saver_saved_entity | entity_id | 184 | | **restore_state** | event_saver_restored | entity_id | 185 | | **delete** | event_saver_deleted_entity | entity_id | 186 | | **clear** | event_saver_cleared | | 187 | | **set_variable** | event_saver_saved_variable | variable, value | 188 | | **delete_variable** | event_saver_deleted_variable | variable | 189 | 190 | The events can be used to trigger further automations that depend on the completion of the services. The documentation is provided [here](https://www.home-assistant.io/docs/automation/trigger/#event-trigger). 191 | 192 | 193 | 194 | 195 | ## Support 196 | 197 | If you want to support my work with a donation you can use one of the following platforms: 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 212 | 215 | 219 | 220 | 221 | 222 | 226 | 229 | 230 | 231 | 232 | 233 | 236 | 239 | 242 | 243 | 244 | 245 | 249 | 252 | 255 | 256 |
PlatformPayment methodsLinkComment
Ko-fi 209 |
  • PayPal
  • 210 |
  • Credit card
  • 211 |
    213 | Buy Me a Coffee at ko-fi.com 214 | 216 |
  • No fees
  • 217 |
  • Single or monthly payment
  • 218 |
    buycoffee.to 223 |
  • BLIK
  • 224 |
  • Bank transfer
  • 225 |
    227 | Postaw mi kawę na buycoffee.to 228 |
    PayPal 234 |
  • PayPal
  • 235 |
    237 | PayPal Logo 238 | 240 |
  • No fees
  • 241 |
    Revolut 246 |
  • Revolut
  • 247 |
  • Credit Card
  • 248 |
    250 | Revolut 251 | 253 |
  • No fees
  • 254 |
    257 | 258 | ### Powered by 259 | [![PyCharm logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSourceSupport) 260 | 261 | 262 | [ko_fi_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Ko-Fi&color=F16061&logo=ko-fi&logoColor=white 263 | 264 | [ko_fi]: https://ko-fi.com/piotrmachowski 265 | 266 | [buycoffee_to_shield]: https://shields.io/badge/buycoffee.to-white?style=flat&labelColor=white&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABhmlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpaIVh1YQcchQnayIijhKFYtgobQVWnUweemP0KQhSXFxFFwLDv4sVh1cnHV1cBUEwR8QVxcnRRcp8b6k0CLGC4/3cd49h/fuA4R6malmxzigapaRisfEbG5FDLzChxB6MIZ+iZl6Ir2QgWd93VM31V2UZ3n3/Vm9St5kgE8knmW6YRGvE09vWjrnfeIwK0kK8TnxqEEXJH7kuuzyG+eiwwLPDBuZ1BxxmFgstrHcxqxkqMRTxBFF1ShfyLqscN7irJarrHlP/sJgXltOc53WEOJYRAJJiJBRxQbKsBClXSPFRIrOYx7+QcefJJdMrg0wcsyjAhWS4wf/g9+zNQuTE25SMAZ0vtj2xzAQ2AUaNdv+PrbtxgngfwautJa/UgdmPkmvtbTIEdC3DVxctzR5D7jcAQaedMmQHMlPSygUgPcz+qYcELoFulfduTXPcfoAZGhWSzfAwSEwUqTsNY93d7XP7d+e5vx+AIahcq//o+yoAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH5wETCy4vFNqLzwAAAVpJREFUOMvd0rFLVXEYxvHPOedKJnKJhrDLuUFREULE7YDCMYj+AydpsCWiaKu29hZxiP4Al4aWwC1EdFI4Q3hqEmkIBI8ZChWXKNLLvS0/Qcza84V3enm/7/s878t/HxGkeTaIGziP+EB918nawu7Dq1d0e1+2J2bepnk2jFEUVVF+qKV51o9neBCaugfge70keoxxUbSWjrQ+4SUyzKZ5NlnDZdzGG7w4DIh+dtZEFntDA98l8S0MYwctNGrYz9WqKJePFLq80g5Sr+EHlnATp+NA+4qLaZ7FfzMrzbMBjGEdq8GrJMZnvAvFC/8wfAwjWMQ8XmMzaW9sdevNRgd3MFhvNpbaG1u/Dk2/hOc4gadVUa7Um425qii/7Z+xH9O4jwW8Cqv24Tru4hyeVEU588cfBMgpPMI9nMFe0BkFzVOYrYqycyQgQJLwTC2cDZCPeF8V5Y7jGb8BUpRicy7OU5MAAAAASUVORK5CYII= 267 | 268 | [buycoffee_to]: https://buycoffee.to/piotrmachowski 269 | 270 | [buy_me_a_coffee_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Buy%20me%20a%20coffee&color=6f4e37&logo=buy%20me%20a%20coffee&logoColor=white 271 | 272 | [buy_me_a_coffee]: https://www.buymeacoffee.com/PiotrMachowski 273 | 274 | [paypal_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=PayPal.Me&logo=paypal 275 | 276 | [paypal_me]: https://paypal.me/PiMachowski 277 | 278 | [revolut_me_shield]: https://img.shields.io/static/v1.svg?label=%20&message=Revolut&logo=revolut 279 | 280 | [revolut_me]: https://revolut.me/314ma 281 | -------------------------------------------------------------------------------- /custom_components/saver/__init__.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Any, Callable, Sequence 3 | 4 | from homeassistant.config_entries import ConfigEntry 5 | from homeassistant.core import Context, HomeAssistant, ServiceCall 6 | from homeassistant.helpers.restore_state import RestoreEntity 7 | from homeassistant.helpers.script import Script, SCRIPT_MODE_PARALLEL 8 | from homeassistant.helpers.entity_component import EntityComponent 9 | from homeassistant.helpers.template import _get_state_if_valid, Template, TemplateEnvironment 10 | 11 | from .const import * 12 | 13 | _LOGGER = logging.getLogger(__name__) 14 | CONFIG_SCHEMA = SAVER_SCHEMA 15 | 16 | 17 | def setup(hass, config) -> bool: 18 | if DOMAIN not in config: 19 | return True 20 | return setup_entry(hass, config) 21 | 22 | 23 | async def async_setup_entry(hass, config_entry): 24 | result = await hass.async_add_executor_job(setup_entry, hass, config_entry) 25 | return result 26 | 27 | 28 | class SaverVariableTemplate: 29 | def __init__(self, hass: HomeAssistant, entity_id: str) -> None: 30 | self._hass = hass 31 | self._entity_id = entity_id 32 | 33 | def __call__(self, variable: str) -> Any: 34 | saver_state = _get_state_if_valid(self._hass, self._entity_id) 35 | if saver_state is None: 36 | return None 37 | variables = saver_state.attributes["variables"] 38 | if variable in variables: 39 | return variables[variable] 40 | return None 41 | 42 | def __repr__(self) -> str: 43 | return "