Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Meraki AP Device tracker #10749

Closed
wants to merge 20 commits into from
Closed

Meraki AP Device tracker #10749

wants to merge 20 commits into from

Conversation

masarliev
Copy link
Contributor

@masarliev masarliev commented Nov 22, 2017

Description:

Track devices with Meraki AP. More info here

Pull request in home-assistant.github.io with documentation (if applicable): home-assistant/home-assistant.io#4044

Example entry for configuration.yaml (if applicable):

device_tracker:
  - platform: meraki
    secret: secret
    validator: validator

Checklist:

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • Local tests with tox run successfully. Your PR cannot be merged unless tests pass
  • New dependencies have been added to the REQUIREMENTS variable (example).
  • New dependencies are only imported inside functions that use them (example).
  • New dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

gps_location = (lat, lng)
attrs = {}
if ((not self.track_new and clientMac.upper() not in self.devs_to_track) and
clientMac.upper() not in self.devs_to_track):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

visually indented line with same indent as next logical line

_LOGGER.debug("clientMac: %s", clientMac)
gps_location = (lat, lng)
attrs = {}
if ((not self.track_new and clientMac.upper() not in self.devs_to_track) and

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (92 > 79 characters)

if data['secret'] != self.secret:
_LOGGER.error("Invalid Secret received from Meraki")
elif data['version'] != VERSION:
_LOGGER.error("Invalid API version received: %s", data['version'])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (82 > 79 characters)

from homeassistant.const import (HTTP_BAD_REQUEST, HTTP_UNPROCESSABLE_ENTITY)
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.device_tracker import (
PLATFORM_SCHEMA, SOURCE_TYPE_ROUTER, CONF_TRACK_NEW, YAML_DEVICES, load_config, DEFAULT_TRACK_NEW)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (103 > 79 characters)

gps_location = (lat, lng)
attrs = {}
if ((not self.tn and mac.upper() not in self.devices) and
mac.upper() not in self.devices):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continuation line over-indented for visual indent

name = 'api:meraki'

def __init__(self, config, see, devs_to_track, track_new):
"""Initialize Locative URL endpoints."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale docstring.



def setup_scanner(hass, config, see, discovery_info=None):
"""Set up an endpoint for the Locative application."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale docstring.

@asyncio.coroutine
def get(self, request):
"""Meraki message received as GET."""
_LOGGER.info("Merakicmx message received as a GET")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That very verbose.

def _handle(self, hass, data):
if len(data["data"]["observations"]) == 0:
_LOGGER.debug("No observations found")
else:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change it to a guard clause and return. This will allow you to get rid of the else and the indent of the whole code block.

mac.upper() not in self.devices):
_LOGGER.debug("Skipping: %s", mac)
continue
if 'os' in i:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A dictionary lookup could help here to make it shorter.

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_VALIDATOR): cv.string,
vol.Required(CONF_SECRET): cv.string,
vol.Optional(CONF_TRACK_NEW): cv.boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, that is a core function

})


def setup_scanner(hass, config, see, discovery_info=None):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the async interface for that function

if i.get('ssid', False):
attrs['ssid'] = i['ssid']
yield from hass.async_add_job(
partial(self.see,
Copy link
Member

@pvizeli pvizeli Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use async_see from async interface. Remove the partial and don't yield on it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be:

yield from async_see(...)

?

assert req.status == 200
state_name = hass.states.get('{}.{}'.format('device_tracker',
'0026abb8a9a4')).state
assert 'home' == state_name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no newline at end of file

"secret": "secret",
"type": "DevicesSeen",
"data": {
"observations":[

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing whitespace after ':'

import json
from unittest.mock import patch
import pytest
from homeassistant.components.device_tracker.meraki import CONF_VALIDATOR, CONF_SECRET

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (86 > 79 characters)

vol.Required(CONF_SECRET): cv.string
})

@asyncio.coroutine

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expected 2 blank lines, found 1

state_name = hass.states.get('{}.{}'.format('device_tracker',
'0026abb8a9a4')).state
assert 'home' == state_name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line at end of file
blank line contains whitespace

state_name = hass.states.get('{}.{}'.format('device_tracker',
'0026abb8a9a4')).state
assert 'home' == state_name

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blank line at end of file
blank line contains whitespace

@asyncio.coroutine
def async_setup_scanner(hass, config, async_see, discovery_info=None):
"""Set up an endpoint for the Meraki tracker."""
track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to handle this at all. It's done in the device tracker base component.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I used this option to check if device should be added to known. Since meraki floods known devices I will use other option

_LOGGER.debug("clientMac: %s", mac)
gps_location = (lat, lng)
attrs = {}
if ((not self.track and mac.upper() not in self.devices) and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove this whole check.

track_new = config.get(CONF_TRACK_NEW, DEFAULT_TRACK_NEW)
yaml_path = hass.config.path(YAML_DEVICES)
devs_to_track = []
for device in load_config(yaml_path, hass, 0):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So then you don't need this either.

url = URL
name = 'api:meraki'

def __init__(self, config, async_see, devs_to_track, track_new):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And you don't need to pass devs_to_track and track_new.

attrs['seenTime'] = i['seenTime']
if i.get('ssid', False):
attrs['ssid'] = i['ssid']
yield from self.async_see(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make simple:
hass.async_add_job(self.async_see(...))

So it will not block on process and is faster and make that _handle will be a normal function they can add a decorator @callback from core.py.

if len(data["data"]["observations"]) == 0:
_LOGGER.debug("No observations found")
return
res = yield from self._handle(request.app['hass'], data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see no return value from self._handle ?

@addebc
Copy link

addebc commented Dec 1, 2017

Hey Great component however,
Maybe cleanup error messages before PR?

2017-12-01 16:55:59 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 416, in start
    resp = yield from self._request_handler(request)
  File "/usr/lib/python3.6/site-packages/aiohttp/web.py", line 323, in _handle
    resp = yield from handler(request)
  File "/usr/lib/python3.6/site-packages/homeassistant/components/http/ban.py", line 58, in ban_middleware
    return (yield from handler(request))
  File "/usr/lib/python3.6/asyncio/coroutines.py", line 213, in coro
    res = yield from res
  File "/usr/lib/python3.6/asyncio/coroutines.py", line 213, in coro
    res = yield from res
  File "/usr/lib/python3.6/site-packages/homeassistant/components/http/__init__.py", line 430, in handle
    result = yield from result
  File "/config/custom_components/device_tracker/meraki.py", line 93, in post
    yield from self._handle(request.app['hass'], data)
  File "/config/custom_components/device_tracker/meraki.py", line 101, in _handle
    accuracy = int(float(i["location"]["unc"]))
ValueError: cannot convert float NaN to integer

@masarliev
Copy link
Contributor Author

Can you give me request body. I use it for more than 2 months and haven't received this error

@addebc
Copy link

addebc commented Dec 1, 2017

After i set logging to debug error never appered but then i remembered i cleared all my known hosts. And probably thats why its not appearing... i will keep my eye open. Sorry bout that.

Setting track_new_devices: false does nothing for me... I am guessing having that should only scan and update the hosts already in known_hosts?

Btw, are you on discord?

@masarliev
Copy link
Contributor Author

I haven't updated the documentation. Sorry about that, little busy these days. I misunderstood the option track new devices, so it's removed. May be should be added as feature for all platforms.
No. I don't use discord

@addebc
Copy link

addebc commented Dec 2, 2017

Yes, without such feature home assistant gets cluttered with so many entities that are irrelevant (passersby) 5 hours have me 200 entities when really I just want to keep track of my own.

@addebc
Copy link

addebc commented Dec 2, 2017

If possible add me on gmail-chat.

Copy link
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As @pvizeli requested, see specific changes needed below.

return
yield from self._handle(request.app['hass'], data)

@asyncio.coroutine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the annotation to @callback. Import the annotation from homeassistant/core.py.

attrs['seenTime'] = i['seenTime']
if i.get('ssid', False):
attrs['ssid'] = i['ssid']
yield from hass.async_add_job(self.async_see(
Copy link
Member

@MartinHjelmare MartinHjelmare Dec 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to:

hass.async_add_job(self.async_see(...))

async_add_job is a callback and not a coroutine, so you don't yield from it.

if len(data["data"]["observations"]) == 0:
_LOGGER.debug("No observations found")
return
yield from self._handle(request.app['hass'], data)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you change _handle to a callback function instead of a coroutine (see comment below), change here to:

self._handle(request.app['hass'], data)

@addelovein
Copy link
Contributor

addelovein commented Dec 3, 2017

2017-12-03 17:44:51 DEBUG (MainThread) [homeassistant.components.websocket_api] WS 1930745424: Sending {'id': 2, 'type': 'event', 'event': {'event_type': 'state_changed', 'data': {'entity_id': 'device_tracker.0055da5137b3', 'old_state': <state device_tracker.0055da5137b3=home; source_type=router, latitude=x.xxxx, longitude=x.xxxx, gps_accuracy=26, manufacturer=IEEE Registration..., ipv4=/192.168.0.218, seenTime=2017-12-03T16:44:25Z, ssid=Lovein - Home, os=Generic Linux, friendly_name=0055da5137b3, custom_ui_state_card=state-card-custom-ui @ 2017-12-03T05:55:03.151612+01:00>, 'new_state': <state device_tracker.0055da5137b3=home; source_type=router, latitude=59.1404482, longitude=x.xxxx, gps_accuracy=0, manufacturer=IEEE Registration..., ipv4=/192.168.0.218, seenTime=2017-12-03T16:44:25Z, ssid=Lovein - Home, os=Generic Linux, friendly_name=0055da5137b3, custom_ui_state_card=state-card-custom-ui @ 2017-12-03T05:55:03.151612+01:00>}, 'origin': 'LOCAL', 'time_fired': datetime.datetime(2017, 12, 3, 16, 44, 51, 247281, tzinfo=<UTC>)}}
2017-12-03 17:44:51 INFO (MainThread) [homeassistant.components.http] Serving /api/meraki to ******* (auth: True)
2017-12-03 17:44:51 ERROR (MainThread) [aiohttp.server] Error handling request
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/aiohttp/web_protocol.py", line 416, in start
    resp = yield from self._request_handler(request)
  File "/usr/lib/python3.6/site-packages/aiohttp/web.py", line 323, in _handle
    resp = yield from handler(request)
  File "/usr/lib/python3.6/site-packages/homeassistant/components/http/ban.py", line 58, in ban_middleware
    return (yield from handler(request))
  File "/usr/lib/python3.6/asyncio/coroutines.py", line 213, in coro
    res = yield from res
  File "/usr/lib/python3.6/asyncio/coroutines.py", line 213, in coro
    res = yield from res
  File "/usr/lib/python3.6/site-packages/homeassistant/components/http/__init__.py", line 430, in handle
    result = yield from result
  File "/config/custom_components/device_tracker/meraki.py", line 93, in post
    yield from self._handle(request.app['hass'], data)
  File "/config/custom_components/device_tracker/meraki.py", line 101, in _handle
    accuracy = int(float(i["location"]["unc"]))
ValueError: cannot convert float NaN to integer
2017-12-03 17:44:53 INFO (SyncWorker_8) [homeassistant.components.device_tracker.nmap_tracker] Scanning...

@MartinHjelmare
Copy link
Member

How much coverage percentage is there with these tests? If it's above ~ 95 % you should remove the module from .coveragerc.

@MartinHjelmare
Copy link
Member

Please also add tests for the cases at lines 87 and 91:
https://coveralls.io/builds/14503965/source?filename=homeassistant%2Fcomponents%2Fdevice_tracker%2Fmeraki.py

@MartinHjelmare
Copy link
Member

Great!

@MartinHjelmare
Copy link
Member

Wait, there's an unrelated commit in this PR history that should not be there:
1d3b7c3

Please rebase the changes that are relevant in this branch on top of latest dev.
Then we can merge.

@masarliev
Copy link
Contributor Author

I merged remote dev in this branch and that's why this commit is in it.

@MartinHjelmare
Copy link
Member

MartinHjelmare commented Dec 5, 2017

That commit doesn't exist in upstream dev. Your commit in your PR to update hbmqtt was squashed and merged as:
bd5a16d

You should start from a new branch based on newly synced dev when you start your work for a PR.

Can you rebase from 1a264da to HEAD, onto fresh synced dev? Something like this:

git checkout -b meraki-tracker dev
git branch -D dev
git fetch upstream dev
git checkout dev
git checkout meraki-tracker
git rebase --onto dev 69874075cbb55ab6b952b82c18e18ec6a31d3770 meraki-tracker

6987407 is the merge commit before your relevant work in this PR.

Edit:
Then you can open a new PR and we can merge that one. Switching branches will confuse github, so I don't think you can continue in this PR.

@masarliev
Copy link
Contributor Author

Closing this PR. New one is here #10971

@masarliev masarliev closed this Dec 5, 2017
@home-assistant home-assistant locked and limited conversation to collaborators Mar 30, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants