Skip to content

Add additional sensors for Arlo Baby camera#15074

Merged
MartinHjelmare merged 20 commits intohome-assistant:devfrom
lukiffer:arlo-baby
Jul 6, 2018
Merged

Add additional sensors for Arlo Baby camera#15074
MartinHjelmare merged 20 commits intohome-assistant:devfrom
lukiffer:arlo-baby

Conversation

@lukiffer
Copy link
Copy Markdown
Contributor

@lukiffer lukiffer commented Jun 21, 2018

Description:

Adds additional temperature, humidity, and air quality sensors for Arlo Baby cameras implemented in pyarlo==0.1.8.

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

Example entry for configuration.yaml (if applicable):

sensor:
  - platform: arlo
    monitored_conditions:
      - temperature
      - humidity
      - air_quality

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox. Your PR cannot be merged unless tests pass

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

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

  • New dependencies have been added to the REQUIREMENTS variable (example).
  • New dependencies are only imported inside functions that use them (example).
  • New or updated dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

If the code does not interact with devices:

  • Tests have been added to verify that the new code works.

Comment thread homeassistant/components/sensor/arlo.py Outdated
for base_station in arlo.base_stations:
if ((sensor_type == 'temperature' or \
sensor_type == 'humidity' or \
sensor_type == 'air_quality') and \
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

the backslash is redundant between brackets

Comment thread homeassistant/components/sensor/arlo.py Outdated

for base_station in arlo.base_stations:
if ((sensor_type == 'temperature' or \
sensor_type == 'humidity' or \
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

the backslash is redundant between brackets

Comment thread homeassistant/components/sensor/arlo.py Outdated
sensors.append(ArloSensor(name, camera, sensor_type))

for base_station in arlo.base_stations:
if ((sensor_type == 'temperature' or \
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

the backslash is redundant between brackets

Comment thread homeassistant/components/sensor/arlo.py Outdated
sensor_type == 'humidity' or \
sensor_type == 'air_quality':
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

blank line contains whitespace

@lukiffer lukiffer changed the title Add additional sensors for Arlo Baby camera WIP Add additional sensors for Arlo Baby camera Jun 21, 2018
Copy link
Copy Markdown
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.

@tchellomello could you have a look and review?

Comment thread homeassistant/components/sensor/arlo.py Outdated
try:
self._state = self._data.battery_level
except TypeError:
except (AttributeError, TypeError):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would there be an AttributeError?

Copy link
Copy Markdown
Contributor

@tchellomello tchellomello Jun 24, 2018

Choose a reason for hiding this comment

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

Yeah I don't think the attribute error should be necessary since all cameras have the battery_level

@tchellomello
Copy link
Copy Markdown
Contributor

It looks good to me. I don't have a baby monitor but code looks good!

@lukiffer
Copy link
Copy Markdown
Contributor Author

This PR may have been slightly premature. I’ve since discovered an issue with stats not updating. I’m working on resolving this before removing the WIP designation. If it would be preferable to close this PR and reopen one once this issue is resolved, feel free to close this.

Copy link
Copy Markdown
Contributor

@tchellomello tchellomello left a comment

Choose a reason for hiding this comment

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

@lukiffer could you bump the Pyarlo module to pyarlo==0.1.9 .

https://pypi.org/project/pyarlo/0.1.9/

Thanks!

Comment thread tests/components/sensor/test_arlo.py Outdated
})
sensor = TestArloSensor._get_mock_sensor('test', 'humidity', data)
attrs = sensor.device_state_attributes
self.assertEqual(attrs.get(ATTR_ATTRIBUTION), 'Data provided by arlo.netgear.com')
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (90 > 79 characters)

Comment thread tests/components/sensor/test_arlo.py Outdated
data = TestArloSensor._get_named_tuple({
'captured_today': [0, 0, 0, 0, 0]
})
sensor = TestArloSensor._get_mock_sensor('Captured Today', 'captured_today', data)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (90 > 79 characters)

Comment thread tests/components/sensor/test_arlo.py Outdated
data = TestArloSensor._get_named_tuple({
'cameras': [0, 0]
})
sensor = TestArloSensor._get_mock_sensor('Arlo Cameras', 'total_cameras', data)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (87 > 79 characters)

Comment thread tests/components/sensor/test_arlo.py Outdated
"""Test the unit_of_measurement property."""
sensor = TestArloSensor._get_mock_sensor()
self.assertIsNone(sensor.unit_of_measurement)
sensor = TestArloSensor._get_mock_sensor('Battery Level', 'battery_level')
Copy link
Copy Markdown

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)

Comment thread tests/components/sensor/test_arlo.py Outdated
'battery_level': 50
})

sensor = TestArloSensor._get_mock_sensor('Battery Level', 'battery_level', data)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (88 > 79 characters)

Comment thread tests/components/sensor/test_arlo.py Outdated
self.assertEqual(sensor.name, 'Last')

@asyncio.coroutine
@patch('homeassistant.helpers.dispatcher.async_dispatcher_connect', MagicMock())
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

line too long (84 > 79 characters)

Comment thread tests/components/sensor/test_arlo.py Outdated
TEST_USERNAME = 'test@domain.com'
TEST_PASSWORD = 'password'

class TestArloSensor(unittest.TestCase):
Copy link
Copy Markdown

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

@lukiffer lukiffer changed the title WIP Add additional sensors for Arlo Baby camera Add additional sensors for Arlo Baby camera Jul 2, 2018
@lukiffer
Copy link
Copy Markdown
Contributor Author

lukiffer commented Jul 2, 2018

@tchellomello @MartinHjelmare above comments addressed and ready for review.

Copy link
Copy Markdown
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.

I think the tests would be cleaner if they would be converted to pytest standalone test functions and using pytest fixtures. But it's not a requirement from my side.

Comment thread homeassistant/components/sensor/arlo.py Outdated
@callback
def _update_callback(self):
"""Call update method."""
_LOGGER.debug('update')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remove this. The state update will be logged by the core at info level.

Comment thread homeassistant/components/sensor/arlo.py Outdated
self._sensor_type == 'captured_today' or \
self._sensor_type == 'battery_level' or \
self._sensor_type == 'signal_strength':
known_sensor_types = [
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This could be moved to a constant at module level.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was a lazy workaround for address a maximum conditions in an if statement on my part. There's a dictionary with keys of known types already at the module level, I'll use that.

Comment thread homeassistant/components/sensor/arlo.py Outdated
elif self._sensor_type == 'temperature':
try:
self._state = self._data.ambient_temperature
except (AttributeError, TypeError):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would there be these errors? The library shouldn't let a type error bubble up from a simple attribute access. All attributes should be initialized after init so attribute error shouldn't be possible either. If it is, the library should be updated to handle that better.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I had these in there because the library was in some cases bubbling up an AttributeError but in further testing, it appears to be the result of an issue that was fixed in pyarlo==0.1.9 where the method to get the ambient sensor data was not being called on update. I've removed them and will push.

Comment thread tests/components/sensor/test_arlo.py Outdated
sensor.update()
self.assertEqual(sensor.state, 2)

def test_update_caputred_today(self):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Typo.

Comment thread tests/components/sensor/test_arlo.py Outdated

def test_setup_with_no_data(self):
"""Test setup_platform with no data."""
result = arlo.setup_platform(self.hass, None, None)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

setup_platform shouldn't return anything. Please fix that in the module and update this test.

Comment thread tests/components/sensor/test_arlo.py Outdated

def test_sensor_name(self):
"""Test the name property."""
sensor = TestArloSensor._get_mock_sensor()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The method is weirdly named since it doesn't seem to return a mock sensor but an actual instance of an arlo sensor.

Access methods using self..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will correct the method name and change to self access.

Also, just for reference as I'm coming back to python several years away, is it not optimal to use the @staticmethod decorator and treat these as "static" as opposed to members? Or is this just convention? Or is this an artifact of having this in a class as opposed to just standalone functions? Thanks for the help.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Static methods can still be accessed on the instance, ie using self. inside instance methods. It's only inside the static method that the instance isn't available.

Yeah, the static methods are part of what could be cleaned up if we would move to standalone pytest tests.

Comment thread tests/components/sensor/test_arlo.py Outdated
sensor = TestArloSensor._get_mock_sensor()
self.assertEqual(sensor.name, 'Last')

@asyncio.coroutine
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Remove this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Again, having just come back to python (after several years and from python2), async methods in python3 have come quite a long way, and I'm still getting accustomed to the new stuff:

When I omit this, it complains that the function is never awaited. In tests for other HA components, I noticed that standalone functions omit this, but functions declared in classes have it. Is this correlation true or just coincidence? If it does in fact need to be removed, what's the appropriate way to address the no-await issue?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We're already declaring the method a coroutine by saying async def. This is the new async syntax in Python 3.5. So we shouldn't add the coroutine decorator on top of that. Class based unittest tests don't support coroutines as test methods. Here we have to move to standalone pytest tests.

Comment thread tests/components/sensor/test_arlo.py Outdated
"""Test dispatcher called when added."""
sensor = TestArloSensor._get_mock_sensor()
await sensor.async_added_to_hass()
dispatcher.async_dispatcher_connect.assert_called_once()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We prefer not to use assert_called_once but instead use assert len(mock.calls) == 1. A typo on the mock method would pass silently.

Comment thread tests/components/sensor/test_arlo.py Outdated

def test_sensor_state_default(self):
"""Test the state property."""
sensor = TestArloSensor._get_mock_sensor()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use self._get_mock_sensor().

Comment thread tests/components/sensor/test_arlo.py Outdated
sensors = None

def _add_devices(self, sensors, boolean):
if boolean:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why would we only add the sensors when passing True as the second argument? That's not how the actual code works.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not actually sure what the second param does -- just noticed that it's hardcoded True in the sensor code. But this does make the test rather brittle and potentially (probably) incorrect. I've removed the condition for now and will take another pass after some discovery.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Passing True to add_devices calls entity.update during entity addition.

@lukiffer lukiffer changed the title Add additional sensors for Arlo Baby camera WIP Add additional sensors for Arlo Baby camera Jul 3, 2018
@lukiffer
Copy link
Copy Markdown
Contributor Author

lukiffer commented Jul 3, 2018

@MartinHjelmare thanks for the feedback - super helpful insights. I converted to standalone pytest functions and cleaned up some of the other stuff you mentioned.

@lukiffer lukiffer changed the title WIP Add additional sensors for Arlo Baby camera Add additional sensors for Arlo Baby camera Jul 3, 2018
Comment thread homeassistant/components/sensor/arlo.py Outdated
self._sensor_type == 'signal_strength':
known = False
for sensor_type in SENSOR_TYPES:
if self._sensor_type == sensor_type:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We already validate this in the config schema. We can remove this check completely here.

Comment thread tests/components/sensor/test_arlo.py Outdated
def test_async_added_to_hass(default_sensor):
"""Test dispatcher called when added."""
yield from default_sensor.async_added_to_hass()
assert len(dispatcher.async_dispatcher_connect.calls) == 1
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should check that the calls on the mock object is of correct length.

Comment thread tests/components/sensor/test_arlo.py Outdated

@patch('homeassistant.helpers.dispatcher.async_dispatcher_connect',
MagicMock())
def test_async_added_to_hass(default_sensor):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Define this as a coroutine with async def.

Comment thread tests/components/sensor/test_arlo.py Outdated
MagicMock())
def test_async_added_to_hass(default_sensor):
"""Test dispatcher called when added."""
yield from default_sensor.async_added_to_hass()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use await instead of yield from.

Comment thread tests/components/sensor/test_arlo.py Outdated
assert default_sensor.name == 'Last'


@patch('homeassistant.helpers.dispatcher.async_dispatcher_connect',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Move this to a fixture in which we yield the mock.

Comment thread tests/components/sensor/test_arlo.py Outdated
"""Test dispatcher called when added."""
with patch(
'homeassistant.components.sensor.arlo.async_dispatcher_connect',
MagicMock()) as mock:
Copy link
Copy Markdown

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

@lukiffer
Copy link
Copy Markdown
Contributor Author

lukiffer commented Jul 5, 2018

@MartinHjelmare thanks again for the feedback. I fixed a problem where I was patching the wrong namespace -- this helped make sense of a lot of the other feedback you gave. I did however still have one thing I was stuck on -- not sure if it's a blocker though:

I removed the @patch() decorator from the test method intending to move it onto a fixture, but am having an issue with passing the mock by reference on the fixture. When using the with patch() as syntax in the test method, the test works correctly and I've added checks for the call_args.

However, when I pass the sensor and the mock as a fixture, e.g.:

class DispatcherFixture():
    def __init__(self, sensor, mock):
        self.sensor = sensor
        self.mock = mock

@patch('homeassistant.components.sensor.arlo.async_dispatcher_connect',
        MagicMock())
@pytest.fixture()
def dispatcher_fixture(default_sensor, hass):
    # ...
    return DispatcherFixture(default_sensor, arlo.async_dispatcher_connect)

I get dispatcher_fixture.mock as an instance of MagicMock but the only call is [call.__eq__(sentinel.DEFAULT)]. Not sure what's going awry here, but again the test is functional as committed and I don't foresee the fixture being reused for another test.

As an aside, this may be getting a bit out of my league, and given that the code under test (for this specific test) isn't part of the scope of the initial feature addition, perhaps it would be appropriate to redact this test from this PR and file a separate PR with only the appropriate/pythonic implementation of this test. What are your thoughts?

@MartinHjelmare
Copy link
Copy Markdown
Member

It's ok to keep the test as is.

This is how you could have applied the patch within a fixture. Note the yield.

@pytest.fixture
def mock_dispatch():
    with patch('path.to.dispatch') as _mock:
        yield _mock

Copy link
Copy Markdown
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.

It's easier to catch bugs if we set up the complete platform, while patching appropriately, and run all checks on the entity using the home assistant core, eg get the state from the state machine and check that it's ok. Ie write the tests more as integration tests than isolated unit tests.

Comment thread homeassistant/components/sensor/arlo.py Outdated
self._sensor_type == 'battery_level' or \
self._sensor_type == 'signal_strength':
attrs['model'] = self._data.model_id
attrs['model'] = self._data.model_id
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Add a test or update the test where all sensor types are set up, and test that attributes return ok also for the total cameras type. I think we need to add a check here where we don't add the model attribute for that sensor type.

@lukiffer
Copy link
Copy Markdown
Contributor Author

lukiffer commented Jul 5, 2018

@MartinHjelmare I've added the additional test cases you mentioned and updated the dispatcher test to use a fixture -- thanks for giving me the last piece of the puzzle on that one (again). I also added the check for total_cameras sensor type -- good catch.

I'm pretty sure that what you mentioned about having fully-setup integration tests as opposed to unit tests is not satisfied by the latest commits. I can definitely see the benefit of this approach (especially given the total_cameras example you highlighted). I would like to potentially focus on that a bit more in a separate branch/PR given the scope of those changes. Is that acceptable or should I proceed in this branch?

Copy link
Copy Markdown
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.

Looks good!

@lukiffer
Copy link
Copy Markdown
Contributor Author

lukiffer commented Jul 6, 2018

@MartinHjelmare thanks again for all your help with this. @tchellomello could you also give these changes another look? Thanks!

Copy link
Copy Markdown
Contributor

@tchellomello tchellomello left a comment

Choose a reason for hiding this comment

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

Looks good @lukiffer!! Thanks for your contribution!!

Thanks @MartinHjelmare for the code review!!

@MartinHjelmare MartinHjelmare merged commit 0f1bcfd into home-assistant:dev Jul 6, 2018
@ghost ghost removed the in progress label Jul 6, 2018
@lukiffer lukiffer deleted the arlo-baby branch July 9, 2018 20:10
awarecan pushed a commit to awarecan/home-assistant that referenced this pull request Jul 16, 2018
* Add additional sensors for Arlo Baby camera

* Fix linter errors

* Fix linter error

* Add tests for Arlo sensors

* Fix linter errors

* Bump pyarlo dependency to 0.1.9

* Remove unnecessary AttributeError except

* Fix module reference error in py35

* Fix test

* Address PR review concerns

* Convert to standalone pytest methods

* Fix linter errors

* Fix linter errors

* Fix linter errors

* Fix test

* Remove redundant check, fix async test

* Fix linter error

* Added check for total_cameras sensor, added additional attribute tests

* Add missing docstring
@balloob balloob mentioned this pull request Jul 20, 2018
girlpunk pushed a commit to girlpunk/home-assistant that referenced this pull request Sep 4, 2018
* Add additional sensors for Arlo Baby camera

* Fix linter errors

* Fix linter error

* Add tests for Arlo sensors

* Fix linter errors

* Bump pyarlo dependency to 0.1.9

* Remove unnecessary AttributeError except

* Fix module reference error in py35

* Fix test

* Address PR review concerns

* Convert to standalone pytest methods

* Fix linter errors

* Fix linter errors

* Fix linter errors

* Fix test

* Remove redundant check, fix async test

* Fix linter error

* Added check for total_cameras sensor, added additional attribute tests

* Add missing docstring
@home-assistant home-assistant locked and limited conversation to collaborators Dec 10, 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.

5 participants