mirror of
https://github.com/home-assistant/core.git
synced 2025-12-24 04:50:05 +00:00
Consolidate all platforms that have no tests (#22096)
* Consolidate * Fix tests * Update imports * Fix import * Use importlib because integration and package share name * Fix more tests * Update .coveragerc and CODEOWNERS
This commit is contained in:
182
homeassistant/components/imap/sensor.py
Normal file
182
homeassistant/components/imap/sensor.py
Normal file
@@ -0,0 +1,182 @@
|
||||
"""
|
||||
IMAP sensor support.
|
||||
|
||||
For more details about this platform, please refer to the documentation at
|
||||
https://home-assistant.io/components/sensor.imap/
|
||||
"""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (
|
||||
CONF_NAME, CONF_PASSWORD, CONF_PORT, CONF_USERNAME,
|
||||
EVENT_HOMEASSISTANT_STOP)
|
||||
from homeassistant.exceptions import PlatformNotReady
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['aioimaplib==0.7.15']
|
||||
|
||||
CONF_SERVER = 'server'
|
||||
CONF_FOLDER = 'folder'
|
||||
CONF_SEARCH = 'search'
|
||||
|
||||
DEFAULT_PORT = 993
|
||||
|
||||
ICON = 'mdi:email-outline'
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Optional(CONF_NAME): cv.string,
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Required(CONF_SERVER): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
vol.Optional(CONF_FOLDER, default='INBOX'): cv.string,
|
||||
vol.Optional(CONF_SEARCH, default='UnSeen UnDeleted'): cv.string,
|
||||
})
|
||||
|
||||
|
||||
async def async_setup_platform(hass,
|
||||
config,
|
||||
async_add_entities,
|
||||
discovery_info=None):
|
||||
"""Set up the IMAP platform."""
|
||||
sensor = ImapSensor(config.get(CONF_NAME),
|
||||
config.get(CONF_USERNAME),
|
||||
config.get(CONF_PASSWORD),
|
||||
config.get(CONF_SERVER),
|
||||
config.get(CONF_PORT),
|
||||
config.get(CONF_FOLDER),
|
||||
config.get(CONF_SEARCH))
|
||||
if not await sensor.connection():
|
||||
raise PlatformNotReady
|
||||
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, sensor.shutdown())
|
||||
async_add_entities([sensor], True)
|
||||
|
||||
|
||||
class ImapSensor(Entity):
|
||||
"""Representation of an IMAP sensor."""
|
||||
|
||||
def __init__(self, name, user, password, server, port, folder, search):
|
||||
"""Initialize the sensor."""
|
||||
self._name = name or user
|
||||
self._user = user
|
||||
self._password = password
|
||||
self._server = server
|
||||
self._port = port
|
||||
self._folder = folder
|
||||
self._email_count = None
|
||||
self._search = search
|
||||
self._connection = None
|
||||
self._does_push = None
|
||||
self._idle_loop_task = None
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Handle when an entity is about to be added to Home Assistant."""
|
||||
if not self.should_poll:
|
||||
self._idle_loop_task = self.hass.loop.create_task(self.idle_loop())
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon to use in the frontend."""
|
||||
return ICON
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the number of emails found."""
|
||||
return self._email_count
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return the availability of the device."""
|
||||
return self._connection is not None
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return if polling is needed."""
|
||||
return not self._does_push
|
||||
|
||||
async def connection(self):
|
||||
"""Return a connection to the server, establishing it if necessary."""
|
||||
import aioimaplib
|
||||
|
||||
if self._connection is None:
|
||||
try:
|
||||
self._connection = aioimaplib.IMAP4_SSL(
|
||||
self._server, self._port)
|
||||
await self._connection.wait_hello_from_server()
|
||||
await self._connection.login(self._user, self._password)
|
||||
await self._connection.select(self._folder)
|
||||
self._does_push = self._connection.has_capability('IDLE')
|
||||
except (aioimaplib.AioImapException, asyncio.TimeoutError):
|
||||
self._connection = None
|
||||
|
||||
return self._connection
|
||||
|
||||
async def idle_loop(self):
|
||||
"""Wait for data pushed from server."""
|
||||
import aioimaplib
|
||||
|
||||
while True:
|
||||
try:
|
||||
if await self.connection():
|
||||
await self.refresh_email_count()
|
||||
await self.async_update_ha_state()
|
||||
|
||||
idle = await self._connection.idle_start()
|
||||
await self._connection.wait_server_push()
|
||||
self._connection.idle_done()
|
||||
with async_timeout.timeout(10):
|
||||
await idle
|
||||
else:
|
||||
await self.async_update_ha_state()
|
||||
except (aioimaplib.AioImapException, asyncio.TimeoutError):
|
||||
self.disconnected()
|
||||
|
||||
async def async_update(self):
|
||||
"""Periodic polling of state."""
|
||||
import aioimaplib
|
||||
|
||||
try:
|
||||
if await self.connection():
|
||||
await self.refresh_email_count()
|
||||
except (aioimaplib.AioImapException, asyncio.TimeoutError):
|
||||
self.disconnected()
|
||||
|
||||
async def refresh_email_count(self):
|
||||
"""Check the number of found emails."""
|
||||
if self._connection:
|
||||
await self._connection.noop()
|
||||
result, lines = await self._connection.search(self._search)
|
||||
|
||||
if result == 'OK':
|
||||
self._email_count = len(lines[0].split())
|
||||
else:
|
||||
_LOGGER.error("Can't parse IMAP server response to search "
|
||||
"'%s': %s / %s",
|
||||
self._search, result, lines[0])
|
||||
|
||||
def disconnected(self):
|
||||
"""Forget the connection after it was lost."""
|
||||
_LOGGER.warning("Lost %s (will attempt to reconnect)", self._server)
|
||||
self._connection = None
|
||||
|
||||
async def shutdown(self):
|
||||
"""Close resources."""
|
||||
if self._connection:
|
||||
if self._connection.has_pending_idle():
|
||||
self._connection.idle_done()
|
||||
await self._connection.logout()
|
||||
if self._idle_loop_task:
|
||||
self._idle_loop_task.cancel()
|
||||
Reference in New Issue
Block a user