Skip to content

Commit

Permalink
Code commit & HACS integration.
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamNaj committed Jan 13, 2020
1 parent e8156e2 commit 4005094
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 1 deletion.
50 changes: 50 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Contribution guidelines

Contributing to this project should be as easy and transparent as possible, whether it's:

- Reporting a bug
- Discussing the current state of the code
- Submitting a fix
- Proposing new features

## Github is used for everything

Github is used to host code, to track issues and feature requests, as well as accept pull requests.

Pull requests are the best way to propose changes to the codebase.

1. Fork the repo and create your branch from `master`.
2. If you've changed something, update the documentation.
3. Make sure your code lints (using black).
4. Issue that pull request!

## Any contributions you make will be under the MIT Software License

In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern.

## Report bugs using Github's [issues](../../issues)

GitHub issues are used to track public bugs.
Report a bug by [opening a new issue](../../issues/new/choose); it's that easy!

## Write bug reports with detail, background, and sample code

**Great Bug Reports** tend to have:

- A quick summary and/or background
- Steps to reproduce
- Be specific!
- Give sample code if you can.
- What you expected would happen
- What actually happens
- Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)

People *love* thorough bug reports. I'm not even kidding.

## Use a Consistent Coding Style

Use [black](https://github.com/ambv/black) to make sure the code follows the style.

## License

By contributing, you agree that your contributions will be licensed under its Apache 2.0 License.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2020 Adam Najmanowicz

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,44 @@
# linksys_velop
The linksys_velop platform allows for presence detection by listing devices connected to your Linksys Velop router.

It was tested with a Linksys Velop WHW03v1 Firmware version 1.1.11.197735

## Installation

1. Using the tool of choice open the directory (folder) for your HA configuration (where you find `configuration.yaml`).
2. If you do not have a `custom_components` directory (folder) there, you need to create it.
3. In the `custom_components` directory (folder) create a new folder called `linksys_velop`.
4. Download _all_ the files from the `custom_components/linksys_velop/` directory (folder) in this repository.
5. Place the files you downloaded in the new directory (folder) you created.
6. Restart Home Assistant.
7. Move on to the configuration.

Using your HA configuration directory (folder) as a starting point you should now also have this:

```text
custom_components/linksys_velop/__init__.py
custom_components/linksys_velop/device_tracker.py
custom_components/linksys_velop/manifest.json
```

## Example configuration.yaml

```yaml
device_tracker:
- platform: linksys_velop
host: 192.168.1.1
username: admin
password: YOUR_PASSWORD
```
### Configuration options
Key | Type | Required | Description
-- | -- | -- | --
`host` | `string` | `True` | The hostname or IP address of your access point, e.g., 192.168.1.1.
`username` | `string` | `False` | Defaults to `admin`. You should not have to customize it as Velops deveult to `admin` on login and only allow you to specify password.
`password` | `string` | `True` | The password for your given local admin account.

## Contributions are welcome!

If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md)
1 change: 1 addition & 0 deletions custom_components/linksys_velop/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""The linksys_velop component."""
125 changes: 125 additions & 0 deletions custom_components/linksys_velop/device_tracker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
"""Support for Linksys Velop routers."""
import logging

import requests
import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.device_tracker import (
DOMAIN,
PLATFORM_SCHEMA,
DeviceScanner,
)

from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
from base64 import b64encode as b64

DEFAULT_TIMEOUT = 10

_LOGGER = logging.getLogger(__name__)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Optional(CONF_USERNAME, default="admin"): cv.string,
}
)


def get_scanner(hass, config):
"""Validate the configuration and return a Linksys AP scanner."""
try:
return LinksysSmartWifiDeviceScanner(config[DOMAIN])
except ConnectionError:
return None


class LinksysSmartWifiDeviceScanner(DeviceScanner):
"""This class queries a Linksys Access Point."""

def __init__(self, config):
"""Initialize the scanner."""
self.host = config[CONF_HOST]
self.username = config[CONF_USERNAME]
self.password = config[CONF_PASSWORD]
self.last_results = {}

# Check if the access point is accessible
response = self._make_request()
if not response.status_code == 200:
raise ConnectionError("Cannot connect to Linksys Access Point")

def scan_devices(self):
"""Scan for new devices and return a list with device IDs (MACs)."""
self._update_info()

return self.last_results.keys()

def get_device_name(self, device):
"""Return the name (if known) of the device."""
return self.last_results.get(device)

def _update_info(self):
"""Check for connected devices."""
_LOGGER.info("Checking Linksys Smart Wifi")

self.last_results = {}
response = self._make_request()
if response.status_code != 200:
_LOGGER.error(
"Got HTTP status code %d when getting device list", response.status_code
)
return False
try:
data = response.json()
result = data["responses"][0]
devices = result["output"]["devices"]
for device in devices:
macs = device["knownMACAddresses"]
if not macs:
_LOGGER.warning("Skipping device without known MAC address")
continue
mac = macs[-1]
connections = device["connections"]
if not connections:
_LOGGER.debug("Device %s is not connected", mac)
continue

name = None
for prop in device["properties"]:
if prop["name"] == "userDeviceName":
name = prop["value"]
if not name:
name = device.get("friendlyName", device["deviceID"])

_LOGGER.debug("Device %s is connected", mac)
self.last_results[mac] = name
except (KeyError, IndexError):
_LOGGER.exception("Router returned unexpected response")
return False
return True

def _make_request(self):
# Weirdly enough, this doesn't seem to require authentication
data = [
{
"request": {"sinceRevision": 0},
"action": "http://linksys.com/jnap/devicelist/GetDevices",
}
]

token = b64(bytes(self.username + ":" + self.password, "utf8")).decode("ascii");

headers = {
"X-JNAP-Action": "http://linksys.com/jnap/core/Transaction",
"X-JNAP-Authorization": "Basic " + token
}


return requests.post(
f"http://{self.host}/JNAP/",
timeout=DEFAULT_TIMEOUT,
headers=headers,
json=data,
)
9 changes: 9 additions & 0 deletions custom_components/linksys_velop/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"domain": "linksys_velop",
"name": "Linksys Velop",
"documentation": "https://www.home-assistant.io/components/linksys_velop",
"requirements": [],
"dependencies": [],
"codeowners": ["@adamnaj"],
"homeassistant": "0.100.0"
}
3 changes: 3 additions & 0 deletions hacs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"name": "linksys_velop"
}
25 changes: 25 additions & 0 deletions info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# linksys_velop
The linksys_velop platform allows for presence detection by listing devices connected to your Linksys Velop router.

It was tested with a Linksys Velop WHW03v1 Firmware version 1.1.11.197735

## Example configuration.yaml

```yaml
device_tracker:
- platform: linksys_velop
host: 192.168.1.1
password: YOUR_PASSWORD
```
### Configuration options
Key | Type | Required | Description
-- | -- | -- | --
`host` | `string` | `True` | The hostname or IP address of your access point, e.g., 192.168.1.1.
`username` | `string` | `False` | Defaults to `admin`. You should not have to customize it as Velops deveult to `admin` on login and only allow you to specify password.
`password` | `string` | `True` | The password for your given local admin account.

## Contributions are welcome!

If you want to contribute to this please read the [Contribution guidelines](CONTRIBUTING.md)

0 comments on commit 4005094

Please sign in to comment.