├── .github
├── FUNDING.yml
└── workflows
│ └── validate.yaml
├── LICENSE
├── README.md
├── custom_components
└── hass_agent_notifier
│ ├── __init__.py
│ ├── manifest.json
│ └── notify.py
└── hacs.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: LAB02-Admin
2 | ko_fi: lab02research
3 |
--------------------------------------------------------------------------------
/.github/workflows/validate.yaml:
--------------------------------------------------------------------------------
1 | name: Validate
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: "0 0 * * *"
8 |
9 | jobs:
10 | validate:
11 | runs-on: "ubuntu-latest"
12 | steps:
13 | - uses: "actions/checkout@v2"
14 | - name: HACS validation
15 | uses: "hacs/action@main"
16 | with:
17 | category: "integration"
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) LAB02 Research 2021
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/LAB02-Research/HASS.Agent-Notifier/actions?query=workflow:"Validate")
2 | [](https://github.com/LAB02-Research/HASS.Agent-Notifier/releases/)
3 | [](#license)
4 | [](https://www.buymeacoffee.com/lab02research)
5 | [](https://discord.gg/nMvqzwrVBU)
6 |
7 | [](https://github.com/hacs/integration)
8 |
9 |
10 | # HASS.Agent Notifier
11 |
12 | ⚠️❗**YOU DON'T NEED THIS INTEGRATION IF YOU'RE USING THE NEW [HASS.AGENT](https://github.com/LAB02-Research/HASS.Agent-Integration) INTEGRATION!** ❗⚠️
13 |
14 | ----
15 |
16 | Note: this integrations has been replaced by the [new integration](https://github.com/LAB02-Research/HASS.Agent-Integration). It will keep working, but for new functionality, consider switching to the new one.
17 |
18 | ----
19 |
20 | This Home Assistant integration allows you to send notifications to HASS.Agent, a Windows-based Home Assistant client.
21 |
22 | Need help? Check [the documentation](https://hassagent.readthedocs.io/), visit the dedicated HA forum thread or join on Discord.
23 |
24 | Note: it won't be of much use if you don't have HASS.Agent installed & configured on at least one PC (or Windows based device).
25 |
26 | ----
27 |
28 | ### Contents
29 |
30 | * [Functionality](#functionality)
31 | * [Installation](#installation)
32 | * [Configuration](#configuration)
33 | * [Installation and Configuration Summary](#installation-and-configuration-summary)
34 | * [Usage](#usage)
35 | * [Help](#help)
36 | * [Debugging](#debugging)
37 | * [License](#license)
38 |
39 | ----
40 |
41 | ### Functionality
42 |
43 | Send normal (text-based) and image notifications, and configure them to be *actionable* to directly interact with Home Assistant.
44 |
45 | ----
46 |
47 | ### Installation
48 |
49 | The easiest way to install is to use HACS. Simply search for **HASS.Agent Notifier**, install and restart Home Assistant.
50 |
51 | If you want to manually install, copy the `hass_agent_notifier` folder into the `config\custom_components` folder of your Home Assistant instance, and restart.
52 |
53 | ----
54 |
55 | ### Configuration
56 |
57 | This integration exposes itself as a notifications integration, and can be configured by adding this snippet in your `configuration.yaml` file:
58 |
59 | ```yaml
60 | notify:
61 | name: "hass agent test device"
62 | platform: hass_agent_notifier
63 | resource: http://{device_ip}:5115/notify
64 | ```
65 |
66 | Replace `{device_ip}` with the IP of the device that has an HASS.Agent instance running. To find your IP, run `ipconfig` in a command prompt on your PC. Look for the value after `IPv4 Address`. Optionally replace `5115` if you've configured a different port, normally you shouldn't have to.
67 |
68 | Restart Home Assistant to load your configuration.
69 |
70 | The port needs to be open on the target device. HASS.Agent will offer to do this for you during the onboarding process.
71 | To do so manually, you can run this command in an elevated prompt:
72 |
73 | `netsh advfirewall firewall add rule name="HASS.Agent Notifier" dir=in action=allow protocol=TCP localport=5115`
74 |
75 | ----
76 |
77 | ### Installation and Configuration Summary
78 |
79 | Quick summary to get things working:
80 |
81 | - Install **HASS.Agent-Notifier** integration, either through HACS or manually
82 | - Reboot Home Assistant
83 | - Create a `notify` entity, make sure you enter the right IP for your PC
84 | - Reboot Home Assistant
85 | - Start adding the new entity to your automations & scripts :)
86 |
87 | ----
88 |
89 | ### Usage
90 |
91 | #### General
92 |
93 | Currently, there are four variables you can set:
94 |
95 | * `message`: the message you want to show
96 | * `title`: the title of your popup [optional]
97 | * `image`: http(s) url containing the location of an image [optional]
98 | * `duration`: duration (in seconds) for which the popup will be shown [optional]
99 |
100 | #### Text notification
101 |
102 | ```yaml
103 | action:
104 | - service: notify.hass_agent_test_device
105 | data:
106 | message: "This is a test message."
107 | ```
108 |
109 | #### Text notification with title and duration
110 |
111 | ```yaml
112 | action:
113 | - service: notify.hass_agent_test_device
114 | data:
115 | message: "This is a test message with title and 3 sec duration."
116 | title: "HASS.Agent Test"
117 | data:
118 | duration: 3
119 | ```
120 |
121 | #### Image notification
122 |
123 | ```yaml
124 | action:
125 | - service: notify.hass_agent_test_device
126 | data:
127 | message: "This is a test message with an image."
128 | data:
129 | image: "http://10.0.0.6:1234/jpeg/image.jpg"
130 | ```
131 |
132 | #### Camera Proxy
133 |
134 | As pointed out by [@brianhanifin]( https://github.com/brianhanifin ) in this issue, you can also use Home Assistant's camera proxy. This way you don't have to share the credentials etc. of your camera. Home Assistant will provide a token that's valid for 5 minutes, so it's safe to use.
135 |
136 | Example script:
137 |
138 | ```yaml
139 | notification_test:
140 | alias: Notification Test
141 | variables:
142 | image: |
143 | {%- set image = "http://hass.local:8123" + state_attr("camera.garden","entity_picture") %}
144 | {{ image }}
145 | sequence:
146 | - service: notify.hass_agent_test
147 | data:
148 | title: Test
149 | message: "This is a test message with an image."
150 | data:
151 | image: "{{ image }}"
152 | mode: single
153 | icon: mdi:bell
154 | ```
155 |
156 | Optionally change `hass.local` to the mDNS/IP of your Home Assistant instance, and change `garden` to the name of your camera - or use another variable as provided in the linked issue.
157 |
158 | #### Script GUI examples
159 |
160 | This is the sequence part of a test script to send a text-only message, created through the Home Assistant GUI:
161 |
162 | 
163 |
164 | This is the same script, but with an image added to the notification:
165 |
166 | 
167 |
168 | You can use the new Button Card to trigger your test scripts.
169 |
170 | ----
171 |
172 | ### Help
173 |
174 | There's a section dedicated to notification support in [the documentation](https://hassagent.readthedocs.io/). It'll help you troubleshoot common problems, and provide some examples.
175 |
176 | ----
177 |
178 | ### Debugging
179 |
180 | **Note: make sure you check [the documentation](https://hassagent.readthedocs.io/) for common troubleshooting help.**
181 |
182 | If something's not working as it should, while everything's configured and HASS.Agent isn't showing any errors in its logs, browse to the following URL from another PC on the same network as HASS.Agent: `http://{hass_agent_ip}:5115`. Make sure to change `{hass_agent_ip}` to the IP of the PC where HASS.Agent's installed.
183 |
184 | If HASS.Agent is configured and the firewall rule's active, you'll see: `HASS.Agent Active`.
185 |
186 | If not, something is blocking access to HASS.Agent. Add the following snippet to your configuration.yaml to enable debug logging for the integration:
187 |
188 |
189 | ```yaml
190 | logger:
191 | default: warning
192 | logs:
193 | custom_components.hass_agent_notifier: debug
194 | ```
195 |
196 | Reboot Home Assistant. Whenever you send a message, this should show up in your logs:
197 |
198 | 
199 |
200 | If not, please open a ticket and post your log output.
201 |
202 | ----
203 |
204 | ### License
205 |
206 | HASS.Agent Notifier and HASS.Agent are released under the MIT license.
207 |
--------------------------------------------------------------------------------
/custom_components/hass_agent_notifier/__init__.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/custom_components/hass_agent_notifier/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "domain": "hass_agent_notifier",
3 | "name": "HASS.Agent Notifier",
4 | "documentation": "https://github.com/LAB02-Research/HASS.Agent-Notifier",
5 | "issue_tracker": "https://github.com/LAB02-Research/HASS.Agent-Notifier/issues",
6 | "dependencies": [],
7 | "codeowners": ["@LAB02-Admin"],
8 | "version": "2022.10.25.1",
9 | "requirements": [],
10 | "iot_class": "local_push"
11 | }
12 |
--------------------------------------------------------------------------------
/custom_components/hass_agent_notifier/notify.py:
--------------------------------------------------------------------------------
1 | """
2 | Custom component for Home Assistant to enable sending messages via HASS Agent.
3 |
4 |
5 | Example configuration.yaml entry:
6 |
7 | notify:
8 | - name: hass notifier
9 | platform: hass_agent_notifier
10 | resource: http://192.168.0.1:5115/notify
11 |
12 | With this custom component loaded, you can send messaged to a HASS Agent.
13 | """
14 |
15 | import logging
16 | from typing import Any
17 | from contextlib import suppress
18 |
19 | import requests
20 | import voluptuous as vol
21 | import re
22 |
23 | from homeassistant.components.notify import (
24 | ATTR_TITLE_DEFAULT,
25 | ATTR_TITLE,
26 | ATTR_DATA,
27 | PLATFORM_SCHEMA,
28 | BaseNotificationService,
29 | )
30 |
31 | from homeassistant.components import media_source
32 |
33 | from homeassistant.helpers.network import NoURLAvailableError, get_url
34 |
35 | from http import HTTPStatus
36 |
37 | from homeassistant.const import (
38 | CONF_RESOURCE,
39 | )
40 | import homeassistant.helpers.config_validation as cv
41 |
42 | PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({vol.Required(CONF_RESOURCE): cv.url})
43 |
44 | _LOGGER = logging.getLogger(__name__)
45 |
46 | CAMERA_PROXY_REGEX = re.compile(r"\/api\/camera_proxy\/camera\.(.*)")
47 |
48 |
49 | def get_service(hass, config, discovery_info=None):
50 | """Get the HASS Agent notification service."""
51 | resource = config.get(CONF_RESOURCE)
52 |
53 | _LOGGER.info("Service created")
54 |
55 | return HassAgentNotificationService(hass, resource)
56 |
57 |
58 | class HassAgentNotificationService(BaseNotificationService):
59 | """Implementation of the HASS Agent notification service"""
60 |
61 | def __init__(self, hass, resource):
62 | """Initialize the service."""
63 | self._resource = resource
64 | self._hass = hass
65 |
66 | def send_request(self, url, data):
67 | """Sends the json request"""
68 | return requests.post(url, json=data, timeout=10)
69 |
70 | async def async_send_message(self, message: str, **kwargs: Any):
71 | """Send the message to the provided resource."""
72 | _LOGGER.debug("Preparing notification")
73 |
74 | title = kwargs.get(ATTR_TITLE, ATTR_TITLE_DEFAULT)
75 | data = kwargs.get(ATTR_DATA, None)
76 |
77 | if data is None:
78 | data = dict()
79 |
80 | image = data.get("image", None)
81 |
82 | if image is not None:
83 | new_url = None
84 |
85 | camera_proxy_match = CAMERA_PROXY_REGEX.match(image)
86 |
87 | if camera_proxy_match is not None:
88 | camera = self.hass.states.get(f"camera.{camera_proxy_match.group(1)}")
89 |
90 | if camera is not None:
91 | external_url = None
92 | with suppress(NoURLAvailableError): # external_url not configured
93 | external_url = get_url(self.hass, allow_internal=False)
94 |
95 | if external_url is not None:
96 | access_token = camera.attributes["access_token"]
97 | new_url = f"{external_url}{image}?token={access_token}"
98 |
99 | elif media_source.is_media_source_id(image):
100 | sourced_media = await media_source.async_resolve_media(self.hass, image)
101 | sourced_media = media_source.async_process_play_media_url(
102 | self.hass, sourced_media.url
103 | )
104 | new_url = sourced_media
105 |
106 | if new_url is not None:
107 | data.update({"image": new_url})
108 |
109 | payload = {"message": message, "title": title, "data": data}
110 |
111 | _LOGGER.debug("Sending notification")
112 |
113 | try:
114 |
115 | response = await self.hass.async_add_executor_job(
116 | self.send_request, self._resource, payload
117 | )
118 |
119 | _LOGGER.debug("Checking result")
120 |
121 | if response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
122 | _LOGGER.error(
123 | "Server error. Response %d: %s",
124 | response.status_code,
125 | response.reason,
126 | )
127 | elif response.status_code == HTTPStatus.BAD_REQUEST:
128 | _LOGGER.error(
129 | "Client error (bad request). Response %d: %s",
130 | response.status_code,
131 | response.reason,
132 | )
133 | elif response.status_code == HTTPStatus.NOT_FOUND:
134 | _LOGGER.debug(
135 | "Server error (not found). Response %d: %s",
136 | response.status_code,
137 | response.reason,
138 | )
139 | elif response.status_code == HTTPStatus.METHOD_NOT_ALLOWED:
140 | _LOGGER.error(
141 | "Server error (method not allowed). Response %d",
142 | response.status_code,
143 | )
144 | elif response.status_code == HTTPStatus.REQUEST_TIMEOUT:
145 | _LOGGER.debug(
146 | "Server error (request timeout). Response %d: %s",
147 | response.status_code,
148 | response.reason,
149 | )
150 | elif response.status_code == HTTPStatus.NOT_IMPLEMENTED:
151 | _LOGGER.error(
152 | "Server error (not implemented). Response %d: %s",
153 | response.status_code,
154 | response.reason,
155 | )
156 | elif response.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
157 | _LOGGER.error(
158 | "Server error (service unavailable). Response %d",
159 | response.status_code,
160 | )
161 | elif response.status_code == HTTPStatus.GATEWAY_TIMEOUT:
162 | _LOGGER.error(
163 | "Network error (gateway timeout). Response %d: %s",
164 | response.status_code,
165 | response.reason,
166 | )
167 | elif response.status_code == HTTPStatus.OK:
168 | _LOGGER.debug(
169 | "Success. Response %d: %s", response.status_code, response.reason
170 | )
171 | else:
172 | _LOGGER.debug(
173 | "Unknown response %d: %s", response.status_code, response.reason
174 | )
175 |
176 | except Exception as ex:
177 | _LOGGER.debug("Error sending message: %s", ex)
178 |
--------------------------------------------------------------------------------
/hacs.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "HASS.Agent Notifier",
3 | "homeassistant": "2021.4",
4 | "render_readme": true
5 | }
6 |
--------------------------------------------------------------------------------