-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b3245b5
Showing
17 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
|
||
.idea/ | ||
|
||
custom_components/ipify/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
The MIT License (MIT) | ||
|
||
Copyright (c) 2023 Skipperro | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Ipify.org Public IP Check Integration | ||
|
||
[](https://github.com/hacs/integration) | ||
[](https://github.com/skipperro/ipify-homeassistant/releases/) | ||
 | ||
|
||
|
||
This integration allows you to check the public IP of your Home Assistant instance. | ||
|
||
This is useful if you want to check if your Home Assistant instance is accessible from the outside | ||
and can be used for automations like updating your DNS records after a new IP is assigned. | ||
|
||
 | ||
|
||
## How it works | ||
|
||
Every 5 minutes the public IP is checked and if it changed, the integration will trigger a value change. | ||
|
||
This value can be used in automation scripts or displayed on dashboards. | ||
|
||
## Installation | ||
|
||
1. Install this integration with HACS (adding repository required), or copy the contents of this | ||
repository into the `custom_components/ipify` directory. | ||
2. Restart Home Assistant. | ||
3. Start the configuration flow: | ||
- [](https://my.home-assistant.io/redirect/config_flow_start?domain=ipify) | ||
- Or: Go to `Configuration` -> `Integrations` and click the `+ Add Integration`. Select `Ipify` from the list. | ||
- If the integration is not found try to refresh the HA page without using cache (Ctrl+F5). | ||
4. Select which IP protocol versions (IPv4 and/or IPv6) you want to check. | ||
|
||
 | ||
|
||
## ToDo | ||
|
||
- Add extra parameter to allow custom interval for checking IP. | ||
- Promote this integration to `HACS Default` and/or `HA Core`. | ||
|
||
## Credits | ||
|
||
- Randall Degges (https://github.com/rdegges): Ipify-api code hosted on ipify.org that allows this integration to work. | ||
- Skipperro: Creating the integration for Home Assistant. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
"""IP Custom Component.""" | ||
import asyncio | ||
import logging | ||
|
||
from homeassistant import config_entries, core | ||
|
||
from .const import DOMAIN | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
|
||
async def async_setup_entry( | ||
hass: core.HomeAssistant, entry: config_entries.ConfigEntry | ||
) -> bool: | ||
"""Set up platform from a ConfigEntry.""" | ||
hass.data.setdefault(DOMAIN, {}) | ||
hass_data = dict(entry.data) | ||
# Registers update listener to update config entry when options are updated. | ||
unsub_options_update_listener = entry.add_update_listener(options_update_listener) | ||
# Store a reference to the unsubscribe function to cleanup if an entry is unloaded. | ||
hass_data["unsub_options_update_listener"] = unsub_options_update_listener | ||
hass.data[DOMAIN][entry.entry_id] = hass_data | ||
|
||
# Forward the setup to the sensor platform. | ||
hass.async_create_task( | ||
hass.config_entries.async_forward_entry_setup(entry, "sensor") | ||
) | ||
return True | ||
|
||
|
||
async def options_update_listener( | ||
hass: core.HomeAssistant, config_entry: config_entries.ConfigEntry | ||
): | ||
"""Handle options update.""" | ||
await hass.config_entries.async_reload(config_entry.entry_id) | ||
|
||
|
||
async def async_unload_entry( | ||
hass: core.HomeAssistant, entry: config_entries.ConfigEntry | ||
) -> bool: | ||
"""Unload a config entry.""" | ||
unload_ok = all( | ||
await asyncio.gather( | ||
*[hass.config_entries.async_forward_entry_unload(entry, "sensor")] | ||
) | ||
) | ||
# Remove options_update_listener. | ||
hass.data[DOMAIN][entry.entry_id]["unsub_options_update_listener"]() | ||
|
||
# Remove config entry from domain. | ||
if unload_ok: | ||
hass.data[DOMAIN].pop(entry.entry_id) | ||
|
||
return unload_ok | ||
|
||
async def async_setup(hass: core.HomeAssistant, config: dict) -> bool: | ||
hass.data.setdefault(DOMAIN, {}) | ||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
"""Config flow for PAJ GPS Tracker integration.""" | ||
from __future__ import annotations | ||
import logging | ||
from typing import Any, Dict, Optional | ||
import homeassistant.helpers.config_validation as cv | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.core import callback | ||
|
||
from .const import DOMAIN | ||
|
||
big_int = vol.All(vol.Coerce(int), vol.Range(min=300)) | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
CONFIG_SCHEMA = vol.Schema( | ||
{ | ||
vol.Required('email', default=''): cv.string, | ||
vol.Required('password', default=''): cv.string, | ||
} | ||
) | ||
|
||
class CustomFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
data: Optional[Dict[str, Any]] | ||
|
||
async def async_step_user(self, user_input: Optional[Dict[str, Any]] = None): | ||
errors: Dict[str, str] = {} | ||
if user_input is not None: | ||
self.data = user_input | ||
# If email is null or empty string, add error | ||
if not self.data['email'] or self.data['email'] == '': | ||
errors['base'] = 'email_required' | ||
# If password is null or empty string, add error | ||
if not self.data['password'] or self.data['password'] == '': | ||
errors['base'] = 'password_required' | ||
if not errors: | ||
return self.async_create_entry(title="PAJ GPS Tracker", data=self.data) | ||
|
||
return self.async_show_form(step_id="user", data_schema=CONFIG_SCHEMA, errors=errors) | ||
|
||
@staticmethod | ||
@callback | ||
def async_get_options_flow(config_entry): | ||
"""Get the options flow for this handler.""" | ||
return OptionsFlowHandler(config_entry) | ||
|
||
class OptionsFlowHandler(config_entries.OptionsFlow): | ||
"""Handles options flow for the component.""" | ||
|
||
def __init__(self, config_entry: config_entries.ConfigEntry) -> None: | ||
self.config_entry = config_entry | ||
|
||
async def async_step_init( | ||
self, user_input: Dict[str, Any] = None | ||
) -> Dict[str, Any]: | ||
errors: Dict[str, str] = {} | ||
|
||
if user_input is not None: | ||
self.data = user_input | ||
# If email is null or empty string, add error | ||
if not self.data['email'] or self.data['email'] == '': | ||
errors['base'] = 'email_required' | ||
# If password is null or empty string, add error | ||
if not self.data['password'] or self.data['password'] == '': | ||
errors['base'] = 'password_required' | ||
if not errors: | ||
return self.async_create_entry(title="PAJ GPS Tracker", data={'email': user_input['email'], 'password': user_input['password']}) | ||
|
||
default_email = '' | ||
if 'email' in self.config_entry.data: | ||
default_email = self.config_entry.data['email'] | ||
if 'email' in self.config_entry.options: | ||
default_email = self.config_entry.options['email'] | ||
default_password = False | ||
if 'password' in self.config_entry.data: | ||
default_password = self.config_entry.data['password'] | ||
if 'password' in self.config_entry.options: | ||
default_password = self.config_entry.options['password'] | ||
|
||
OPTIONS_SCHEMA = vol.Schema( | ||
{ | ||
vol.Required('email', default=default_email): cv.string, | ||
vol.Required('password', default=default_password): cv.string, | ||
} | ||
) | ||
return self.async_show_form(step_id="init", data_schema=OPTIONS_SCHEMA, errors=errors) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
DOMAIN = "pajgps" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"domain": "pajgps", | ||
"name": "PAJ GPS Tracker", | ||
"documentation": "https://github.com/Skipperro/pajgps-homeassistant", | ||
"dependencies": [], | ||
"codeowners": ["Skipperro"], | ||
"requirements": [], | ||
"iot_class": "cloud_polling", | ||
"config_flow": true, | ||
"version": "0.1.0" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"config": { | ||
"error": { | ||
"email_required": "Please enter your e-mail address.", | ||
"password_required": "Please enter your password." | ||
}, | ||
"step": { | ||
"user": { | ||
"data": { | ||
"email": "E-Mail", | ||
"password": "Password" | ||
}, | ||
"description": "Add credentials for PAJ GPS Tracker (finder-portal.com).", | ||
"title": "Authorization" | ||
} | ||
} | ||
}, | ||
"options": { | ||
"error": { | ||
"email_required": "Please enter your e-mail address.", | ||
"password_required": "Please enter your password." | ||
}, | ||
"step": { | ||
"user": { | ||
"data": { | ||
"email": "E-Mail", | ||
"password": "Password" | ||
}, | ||
"description": "Add credentials for PAJ GPS Tracker (finder-portal.com).", | ||
"title": "Authorization" | ||
} | ||
} | ||
} | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import unittest | ||
import custom_components.ipify.sensor as sensor | ||
|
||
class IPSensorTest(unittest.IsolatedAsyncioTestCase): | ||
def setUp(self) -> None: | ||
pass | ||
|
||
def test_validate_ipv4(self): | ||
assert sensor.validate_ipv4('192.168.0.1') | ||
assert sensor.validate_ipv4('123.123.123.123') | ||
assert not sensor.validate_ipv4('300.0.0.1') | ||
assert not sensor.validate_ipv4('300.0.0.-1') | ||
assert not sensor.validate_ipv4('2001:0db8:85a3:0000:0000:8a2e:0370:7334') | ||
assert not sensor.validate_ipv4('1.2.3') | ||
assert not sensor.validate_ipv4('1.2.3.4.5') | ||
assert not sensor.validate_ipv4('abc.def.ghi.jkl') | ||
assert not sensor.validate_ipv4('192.168.abc.def') | ||
assert not sensor.validate_ipv4('') | ||
|
||
def test_validate_ipv6(self): | ||
assert sensor.validate_ipv6('2001:db8::2:1') | ||
assert not sensor.validate_ipv6('2001::2:1') | ||
assert sensor.validate_ipv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334') | ||
assert not sensor.validate_ipv6('2001:0db8:85a3:00000:0000:8a2e:0370:7334') | ||
assert not sensor.validate_ipv6('2001:0db8:85a3:0000:0000:8a2e:0370:ghij') | ||
assert not sensor.validate_ipv6('300.0.0.1') | ||
assert not sensor.validate_ipv6('300.0.0.-1') | ||
assert not sensor.validate_ipv6('192.168.0.1') | ||
assert not sensor.validate_ipv6('1.2.3') | ||
assert not sensor.validate_ipv6('1.2.3.4.5') | ||
assert not sensor.validate_ipv6('') | ||
|
||
async def test_update_ipv4(self): | ||
await self.ipv4.async_update() | ||
if self.ipv4.native_value == None: | ||
return # skip tests if no ipv4 address is found (dev is offline?) | ||
assert sensor.validate_ipv4(self.ipv4.native_value) | ||
|
||
async def test_update_ipv6(self): | ||
await self.ipv6.async_update() | ||
if self.ipv6.native_value == None: | ||
return # skip tests if no ipv6 address is found (dev is offline or uses only ipv4?) | ||
assert len(self.ipv6.native_value) > 5 | ||
isv4 = sensor.validate_ipv4(self.ipv6.native_value) | ||
if not isv4: | ||
assert sensor.validate_ipv6(self.ipv6.native_value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"config": { | ||
"error": { | ||
"email_required": "Please enter your e-mail address.", | ||
"password_required": "Please enter your password." | ||
}, | ||
"step": { | ||
"user": { | ||
"data": { | ||
"email": "E-Mail", | ||
"password": "Password" | ||
}, | ||
"description": "Add credentials for PAJ GPS Tracker (finder-portal.com).", | ||
"title": "Authorization" | ||
} | ||
} | ||
}, | ||
"options": { | ||
"error": { | ||
"email_required": "Please enter your e-mail address.", | ||
"password_required": "Please enter your password." | ||
}, | ||
"step": { | ||
"user": { | ||
"data": { | ||
"email": "E-Mail", | ||
"password": "Password" | ||
}, | ||
"description": "Add credentials for PAJ GPS Tracker (finder-portal.com).", | ||
"title": "Authorization" | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"name": "PAJ GPS Tracker", | ||
"iot_class": "Cloud Polling", | ||
"render_readme": true, | ||
"zip_release": false | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.