From 2d932856893a4999b048fbc8af36cdc7b86052c0 Mon Sep 17 00:00:00 2001 From: Ryan McLean Date: Fri, 27 Oct 2017 15:19:47 +0100 Subject: [PATCH] Linode (#9936) * Fix: Last Played Media Title in plex would stay even when player was idle/off Primary Fix is in the "if self._device" portion. code in "if self._session" is a catch all but i'm not 100% if it is needed. * Fixed lint issues with previous commit * 1st Pass at refactoring plex refresh Moved _media** into clearMedia() which is called in _init_ and at start of refresh. Removed redunant _media_* = None entries Grouped TV Show and Music under single if rather than testing seperately for now. * Fixed invalid name for _clearMedia() Removed another media_* = None entry * Removed print() statements used for debug * Removed unneeded "if" statement * Added Base Support for Linode * Removed unused Attr * Corrected some Typos * updated requirements & coveragerc * added import to prevent var not declared errors in linter * Fixed Typo * Added Switch Component for Linode and corrected some errors if data was blank * Updated api lib to hopefully remove dependancy on enum34 * Fixed Reference error * fix pylint errors * Update linode.py * Update linode.py * Update linode.py * Update linode.py --- .coveragerc | 3 + .../components/binary_sensor/linode.py | 96 +++++++++++++++++ homeassistant/components/linode.py | 98 +++++++++++++++++ homeassistant/components/switch/linode.py | 100 ++++++++++++++++++ requirements_all.txt | 3 + 5 files changed, 300 insertions(+) create mode 100644 homeassistant/components/binary_sensor/linode.py create mode 100644 homeassistant/components/linode.py create mode 100644 homeassistant/components/switch/linode.py diff --git a/.coveragerc b/.coveragerc index cca5f723e73..f7854fa7cb4 100644 --- a/.coveragerc +++ b/.coveragerc @@ -107,6 +107,9 @@ omit = homeassistant/components/lametric.py homeassistant/components/*/lametric.py + homeassistant/components/linode.py + homeassistant/components/*/linode.py + homeassistant/components/lutron.py homeassistant/components/*/lutron.py diff --git a/homeassistant/components/binary_sensor/linode.py b/homeassistant/components/binary_sensor/linode.py new file mode 100644 index 00000000000..8af0318373d --- /dev/null +++ b/homeassistant/components/binary_sensor/linode.py @@ -0,0 +1,96 @@ +""" +Support for monitoring the state of Linode Nodes. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.linode/ +""" +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.binary_sensor import ( + BinarySensorDevice, PLATFORM_SCHEMA) +from homeassistant.components.linode import ( + CONF_NODES, ATTR_CREATED, ATTR_NODE_ID, ATTR_NODE_NAME, + ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, + ATTR_REGION, ATTR_VCPUS, DATA_LINODE) + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Node' +DEFAULT_DEVICE_CLASS = 'moving' +DEPENDENCIES = ['linode'] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_NODES): vol.All(cv.ensure_list, [cv.string]), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Linode droplet sensor.""" + linode = hass.data.get(DATA_LINODE) + nodes = config.get(CONF_NODES) + + dev = [] + for node in nodes: + node_id = linode.get_node_id(node) + if node_id is None: + _LOGGER.error("Node %s is not available", node) + return + dev.append(LinodeBinarySensor(linode, node_id)) + + add_devices(dev, True) + + +class LinodeBinarySensor(BinarySensorDevice): + """Representation of a Linode droplet sensor.""" + + def __init__(self, li, node_id): + """Initialize a new Linode sensor.""" + self._linode = li + self._node_id = node_id + self._state = None + self.data = None + + @property + def name(self): + """Return the name of the sensor.""" + if self.data is not None: + return self.data.label + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + if self.data is not None: + return self.data.status == 'running' + return False + + @property + def device_class(self): + """Return the class of this sensor.""" + return DEFAULT_DEVICE_CLASS + + @property + def device_state_attributes(self): + """Return the state attributes of the Linode Node.""" + if self.data: + return { + ATTR_CREATED: self.data.created, + ATTR_NODE_ID: self.data.id, + ATTR_NODE_NAME: self.data.label, + ATTR_IPV4_ADDRESS: self.data.ipv4, + ATTR_IPV6_ADDRESS: self.data.ipv6, + ATTR_MEMORY: self.data.specs.memory, + ATTR_REGION: self.data.region.country, + ATTR_VCPUS: self.data.specs.vcpus, + } + return {} + + def update(self): + """Update state of sensor.""" + self._linode.update() + if self._linode.data is not None: + for node in self._linode.data: + if node.id == self._node_id: + self.data = node diff --git a/homeassistant/components/linode.py b/homeassistant/components/linode.py new file mode 100644 index 00000000000..9e87c002482 --- /dev/null +++ b/homeassistant/components/linode.py @@ -0,0 +1,98 @@ +""" +Support for Linode. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/linode/ +""" +import logging +from datetime import timedelta + +import voluptuous as vol + +from homeassistant.const import CONF_ACCESS_TOKEN +from homeassistant.util import Throttle +import homeassistant.helpers.config_validation as cv + +REQUIREMENTS = ['linode-api==4.1.4b2'] + +_LOGGER = logging.getLogger(__name__) + +ATTR_CREATED = 'created' +ATTR_NODE_ID = 'node_id' +ATTR_NODE_NAME = 'node_name' +ATTR_IPV4_ADDRESS = 'ipv4_address' +ATTR_IPV6_ADDRESS = 'ipv6_address' +ATTR_MEMORY = 'memory' +ATTR_REGION = 'region' +ATTR_VCPUS = 'vcpus' + +CONF_NODES = 'nodes' + +DATA_LINODE = 'data_li' +LINODE_PLATFORMS = ['binary_sensor', 'switch'] +DOMAIN = 'linode' + +MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=60) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_ACCESS_TOKEN): cv.string, + }), +}, extra=vol.ALLOW_EXTRA) + + +def setup(hass, config): + """Set up the Linode component.""" + import linode + + conf = config[DOMAIN] + access_token = conf.get(CONF_ACCESS_TOKEN) + + _linode = Linode(access_token) + + try: + _LOGGER.info("Linode Profile %s", + _linode.manager.get_profile().username) + except linode.errors.ApiError as _ex: + _LOGGER.error(_ex) + return False + + hass.data[DATA_LINODE] = _linode + + return True + + +class Linode(object): + """Handle all communication with the Linode API.""" + + def __init__(self, access_token): + """Initialize the Linode connection.""" + import linode + + self._access_token = access_token + self.data = None + self.manager = linode.LinodeClient(token=self._access_token) + + def get_node_id(self, node_name): + """Get the status of a Linode Instance.""" + import linode + node_id = None + + try: + all_nodes = self.manager.linode.get_instances() + for node in all_nodes: + if node_name == node.label: + node_id = node.id + except linode.errors.ApiError as _ex: + _LOGGER.error(_ex) + + return node_id + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Use the data from Linode API.""" + import linode + try: + self.data = self.manager.linode.get_instances() + except linode.errors.ApiError as _ex: + _LOGGER.error(_ex) diff --git a/homeassistant/components/switch/linode.py b/homeassistant/components/switch/linode.py new file mode 100644 index 00000000000..91177e32116 --- /dev/null +++ b/homeassistant/components/switch/linode.py @@ -0,0 +1,100 @@ +""" +Support for interacting with Linode nodes. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/switch.linode/ +""" +import logging + +import voluptuous as vol + +import homeassistant.helpers.config_validation as cv +from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) +from homeassistant.components.linode import ( + CONF_NODES, ATTR_CREATED, ATTR_NODE_ID, ATTR_NODE_NAME, + ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, + ATTR_REGION, ATTR_VCPUS, DATA_LINODE) + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['linode'] + +DEFAULT_NAME = 'Node' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_NODES): vol.All(cv.ensure_list, [cv.string]), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Set up the Linode Node switch.""" + linode = hass.data.get(DATA_LINODE) + nodes = config.get(CONF_NODES) + + dev = [] + for node in nodes: + node_id = linode.get_node_id(node) + if node_id is None: + _LOGGER.error("Node %s is not available", node) + return + dev.append(LinodeSwitch(linode, node_id)) + + add_devices(dev, True) + + +class LinodeSwitch(SwitchDevice): + """Representation of a Linode Node switch.""" + + def __init__(self, li, node_id): + """Initialize a new Linode sensor.""" + self._linode = li + self._node_id = node_id + self.data = None + self._state = None + + @property + def name(self): + """Return the name of the switch.""" + if self.data is not None: + return self.data.label + + @property + def is_on(self): + """Return true if switch is on.""" + if self.data is not None: + return self.data.status == 'running' + return False + + @property + def device_state_attributes(self): + """Return the state attributes of the Linode Node.""" + if self.data: + return { + ATTR_CREATED: self.data.created, + ATTR_NODE_ID: self.data.id, + ATTR_NODE_NAME: self.data.label, + ATTR_IPV4_ADDRESS: self.data.ipv4, + ATTR_IPV6_ADDRESS: self.data.ipv6, + ATTR_MEMORY: self.data.specs.memory, + ATTR_REGION: self.data.region.country, + ATTR_VCPUS: self.data.specs.vcpus, + } + return {} + + def turn_on(self, **kwargs): + """Boot-up the Node.""" + if self.data.status != 'running': + self.data.boot() + + def turn_off(self, **kwargs): + """Shutdown the nodes.""" + if self.data.status == 'running': + self.data.shutdown() + + def update(self): + """Get the latest data from the device and update the data.""" + self._linode.update() + if self._linode.data is not None: + for node in self._linode.data: + if node.id == self._node_id: + self.data = node diff --git a/requirements_all.txt b/requirements_all.txt index def41459624..5668e398745 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -417,6 +417,9 @@ lightify==1.0.6 # homeassistant.components.light.limitlessled limitlessled==1.0.8 +# homeassistant.components.linode +linode-api==4.1.4b2 + # homeassistant.components.media_player.liveboxplaytv liveboxplaytv==2.0.0