Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ omit =
homeassistant/components/sensor/fedex.py
homeassistant/components/sensor/fitbit.py
homeassistant/components/sensor/fixer.py
homeassistant/components/sensor/folder.py
homeassistant/components/sensor/fritzbox_callmonitor.py
homeassistant/components/sensor/fritzbox_netmonitor.py
homeassistant/components/sensor/gearbest.py
Expand Down
108 changes: 108 additions & 0 deletions homeassistant/components/sensor/folder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Sensor for monitoring the contents of a folder.

For more details about this platform, refer to the documentation at
https://home-assistant.io/components/sensor.folder/
"""
from datetime import timedelta
import glob
import logging
import os

import voluptuous as vol

from homeassistant.helpers.entity import Entity
import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA

_LOGGER = logging.getLogger(__name__)

CONF_FOLDER_PATHS = 'folder'
CONF_FILTER = 'filter'
DEFAULT_FILTER = '*'

SCAN_INTERVAL = timedelta(seconds=1)

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_FOLDER_PATHS): cv.isdir,
vol.Optional(CONF_FILTER, default=DEFAULT_FILTER): cv.string,
})


def get_files_list(folder_path, filter_term):
"""Return the list of files, applying filter."""
query = folder_path + filter_term
files_list = glob.glob(query)
return files_list


def get_size(files_list):
"""Return the sum of the size in bytes of files in the list."""
size_list = [os.stat(f).st_size for f in files_list]
return sum(size_list)


def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the folder sensor."""
path = config.get(CONF_FOLDER_PATHS)

if not hass.config.is_allowed_path(path):
_LOGGER.error("folder %s is not valid or allowed", path)
else:
folder = Folder(path, config.get(CONF_FILTER))
add_devices([folder], True)


class Folder(Entity):
"""Representation of a folder."""

ICON = 'mdi:folder'

def __init__(self, folder_path, filter_term):
"""Initialize the data object."""
folder_path = os.path.join(folder_path, '') # If no trailing / add it
self._folder_path = folder_path # Need to check its a valid path
self._filter_term = filter_term
self._number_of_files = None
self._size = None
self._name = os.path.split(os.path.split(folder_path)[0])[1]
self._unit_of_measurement = 'MB'

def update(self):
"""Update the sensor."""
files_list = get_files_list(self._folder_path, self._filter_term)
self._number_of_files = len(files_list)
self._size = get_size(files_list)

@property
def name(self):
"""Return the name of the sensor."""
return self._name

@property
def state(self):
"""Return the state of the sensor."""
decimals = 2
size_mb = round(self._size/1e6, decimals)
return size_mb

@property
def icon(self):
"""Icon to use in the frontend, if any."""
return self.ICON

@property
def device_state_attributes(self):
"""Return other details about the sensor state."""
attr = {
'path': self._folder_path,
'filter': self._filter_term,
'number_of_files': self._number_of_files,
'bytes': self._size,
}
return attr

@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return self._unit_of_measurement
64 changes: 64 additions & 0 deletions tests/components/sensor/test_folder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""The tests for the folder sensor."""
import unittest
import os

from homeassistant.components.sensor.folder import CONF_FOLDER_PATHS
from homeassistant.setup import setup_component
from tests.common import get_test_home_assistant


CWD = os.path.join(os.path.dirname(__file__))
TEST_FOLDER = 'test_folder'
TEST_DIR = os.path.join(CWD, TEST_FOLDER)
TEST_TXT = 'mock_test_folder.txt'
TEST_FILE = os.path.join(TEST_DIR, TEST_TXT)


def create_file(path):
"""Create a test file."""
with open(path, 'w') as test_file:
test_file.write("test")


class TestFolderSensor(unittest.TestCase):
"""Test the filesize sensor."""

def setup_method(self, method):
"""Set up things to be run when tests are started."""
self.hass = get_test_home_assistant()
if not os.path.isdir(TEST_DIR):
os.mkdir(TEST_DIR)
self.hass.config.whitelist_external_dirs = set((TEST_DIR))

def teardown_method(self, method):
"""Stop everything that was started."""
if os.path.isfile(TEST_FILE):
os.remove(TEST_FILE)
os.rmdir(TEST_DIR)
self.hass.stop()

def test_invalid_path(self):
"""Test that an invalid path is caught."""
config = {
'sensor': {
'platform': 'folder',
CONF_FOLDER_PATHS: 'invalid_path'}
}
self.assertTrue(
setup_component(self.hass, 'sensor', config))
assert len(self.hass.states.entity_ids()) == 0

def test_valid_path(self):
"""Test for a valid path."""
create_file(TEST_FILE)
config = {
'sensor': {
'platform': 'folder',
CONF_FOLDER_PATHS: TEST_DIR}
}
self.assertTrue(
setup_component(self.hass, 'sensor', config))
assert len(self.hass.states.entity_ids()) == 1
state = self.hass.states.get('sensor.test_folder')
assert state.state == '0.0'
assert state.attributes.get('number_of_files') == 1