From 7e50ccd32a1e231e1fa64ad162d0711b28b09be2 Mon Sep 17 00:00:00 2001 From: Fabian Affolter Date: Fri, 30 Sep 2016 18:30:44 +0200 Subject: [PATCH] Component for Digital Ocean (#3322) * Add Digital Ocean implementation * Remove kernel --- .coveragerc | 3 + .../components/binary_sensor/digital_ocean.py | 91 +++++++++++++++++ homeassistant/components/digital_ocean.py | 86 ++++++++++++++++ .../components/switch/digital_ocean.py | 97 +++++++++++++++++++ requirements_all.txt | 3 + 5 files changed, 280 insertions(+) create mode 100644 homeassistant/components/binary_sensor/digital_ocean.py create mode 100644 homeassistant/components/digital_ocean.py create mode 100644 homeassistant/components/switch/digital_ocean.py diff --git a/.coveragerc b/.coveragerc index 0bfa3fb1a04..c2a0e2636e8 100644 --- a/.coveragerc +++ b/.coveragerc @@ -16,6 +16,9 @@ omit = homeassistant/components/bloomsky.py homeassistant/components/*/bloomsky.py + homeassistant/components/digital_ocean.py + homeassistant/components/*/digital_ocean.py + homeassistant/components/dweet.py homeassistant/components/*/dweet.py diff --git a/homeassistant/components/binary_sensor/digital_ocean.py b/homeassistant/components/binary_sensor/digital_ocean.py new file mode 100644 index 00000000000..821acb2da95 --- /dev/null +++ b/homeassistant/components/binary_sensor/digital_ocean.py @@ -0,0 +1,91 @@ +""" +Support for monitoring the state of Digital Ocean droplets. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/binary_sensor.digital_ocean/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.binary_sensor import ( + BinarySensorDevice, PLATFORM_SCHEMA) +from homeassistant.components.digital_ocean import ( + CONF_DROPLETS, ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME, + ATTR_FEATURES, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, + ATTR_REGION, ATTR_VCPUS) +from homeassistant.loader import get_component +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEFAULT_NAME = 'Droplet' +DEFAULT_SENSOR_CLASS = 'motion' +DEPENDENCIES = ['digital_ocean'] + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_DROPLETS): vol.All(cv.ensure_list, [cv.string]), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Digital Ocean droplet sensor.""" + digital_ocean = get_component('digital_ocean') + droplets = config.get(CONF_DROPLETS) + + dev = [] + for droplet in droplets: + droplet_id = digital_ocean.DIGITAL_OCEAN.get_droplet_id(droplet) + dev.append(DigitalOceanBinarySensor( + digital_ocean.DIGITAL_OCEAN, droplet_id)) + + add_devices(dev) + + +class DigitalOceanBinarySensor(BinarySensorDevice): + """Representation of a Digital Ocean droplet sensor.""" + + def __init__(self, do, droplet_id): + """Initialize a new Digital Ocean sensor.""" + self._digital_ocean = do + self._droplet_id = droplet_id + self._state = None + self.update() + + @property + def name(self): + """Return the name of the sensor.""" + return self.data.name + + @property + def is_on(self): + """Return true if the binary sensor is on.""" + return self.data.status == 'active' + + @property + def sensor_class(self): + """Return the class of this sensor.""" + return DEFAULT_SENSOR_CLASS + + @property + def state_attributes(self): + """Return the state attributes of the Digital Ocean droplet.""" + return { + ATTR_CREATED_AT: self.data.created_at, + ATTR_DROPLET_ID: self.data.id, + ATTR_DROPLET_NAME: self.data.name, + ATTR_FEATURES: self.data.features, + ATTR_IPV4_ADDRESS: self.data.ip_address, + ATTR_IPV6_ADDRESS: self.data.ip_v6_address, + ATTR_MEMORY: self.data.memory, + ATTR_REGION: self.data.region['name'], + ATTR_VCPUS: self.data.vcpus, + } + + def update(self): + """Update state of sensor.""" + self._digital_ocean.update() + + for droplet in self._digital_ocean.data: + if droplet.id == self._droplet_id: + self.data = droplet diff --git a/homeassistant/components/digital_ocean.py b/homeassistant/components/digital_ocean.py new file mode 100644 index 00000000000..b91ec2672af --- /dev/null +++ b/homeassistant/components/digital_ocean.py @@ -0,0 +1,86 @@ +""" +Support for Digital Ocean. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/digital_ocean/ +""" +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 = ['python-digitalocean==1.9.0'] + +_LOGGER = logging.getLogger(__name__) + +ATTR_CREATED_AT = 'created_at' +ATTR_DROPLET_ID = 'droplet_id' +ATTR_DROPLET_NAME = 'droplet_name' +ATTR_FEATURES = 'features' +ATTR_IPV4_ADDRESS = 'ipv4_address' +ATTR_IPV6_ADDRESS = 'ipv6_address' +ATTR_MEMORY = 'memory' +ATTR_REGION = 'region' +ATTR_VCPUS = 'vcpus' + +CONF_DROPLETS = 'droplets' + +DIGITAL_OCEAN = None +DIGITAL_OCEAN_PLATFORMS = ['switch', 'binary_sensor'] +DOMAIN = 'digital_ocean' + +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) + + +# pylint: disable=unused-argument,too-few-public-methods +def setup(hass, config): + """Setup the Digital Ocean component.""" + conf = config[DOMAIN] + access_token = conf.get(CONF_ACCESS_TOKEN) + + global DIGITAL_OCEAN + DIGITAL_OCEAN = DigitalOcean(access_token) + + if not DIGITAL_OCEAN.manager.get_account(): + _LOGGER.error("No Digital Ocean account found for the given API Token") + return False + + return True + + +class DigitalOcean(object): + """Handle all communication with the Digital Ocean API.""" + + def __init__(self, access_token): + """Initialize the Digital Ocean connection.""" + import digitalocean + + self._access_token = access_token + self.data = None + self.manager = digitalocean.Manager(token=self._access_token) + + def get_droplet_id(self, droplet_name): + """Get the status of a Digital Ocean droplet.""" + droplet_id = None + + all_droplets = self.manager.get_all_droplets() + for droplet in all_droplets: + if droplet_name == droplet.name: + droplet_id = droplet.id + + return droplet_id + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + def update(self): + """Use the data from Digital Ocean API.""" + self.data = self.manager.get_all_droplets() diff --git a/homeassistant/components/switch/digital_ocean.py b/homeassistant/components/switch/digital_ocean.py new file mode 100644 index 00000000000..8df79bebc5d --- /dev/null +++ b/homeassistant/components/switch/digital_ocean.py @@ -0,0 +1,97 @@ +""" +Support for interacting with Digital Ocean droplets. + +For more details about this component, please refer to the documentation at +https://home-assistant.io/components/switch.digital_ocean/ +""" +import logging + +import voluptuous as vol + +from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA) +from homeassistant.components.digital_ocean import ( + CONF_DROPLETS, ATTR_CREATED_AT, ATTR_DROPLET_ID, ATTR_DROPLET_NAME, + ATTR_FEATURES, ATTR_IPV4_ADDRESS, ATTR_IPV6_ADDRESS, ATTR_MEMORY, + ATTR_REGION, ATTR_VCPUS) +from homeassistant.loader import get_component +import homeassistant.helpers.config_validation as cv + +_LOGGER = logging.getLogger(__name__) + +DEPENDENCIES = ['digital_ocean'] + +DEFAULT_NAME = 'Droplet' + +PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ + vol.Required(CONF_DROPLETS): vol.All(cv.ensure_list, [cv.string]), +}) + + +def setup_platform(hass, config, add_devices, discovery_info=None): + """Setup the Digital Ocean droplet switch.""" + digital_ocean = get_component('digital_ocean') + droplets = config.get(CONF_DROPLETS) + + dev = [] + for droplet in droplets: + droplet_id = digital_ocean.DIGITAL_OCEAN.get_droplet_id(droplet) + dev.append(DigitalOceanSwitch( + digital_ocean.DIGITAL_OCEAN, droplet_id)) + + add_devices(dev) + + +class DigitalOceanSwitch(SwitchDevice): + """Representation of a Digital Ocean droplet switch.""" + + def __init__(self, do, droplet_id): + """Initialize a new Digital Ocean sensor.""" + self._digital_ocean = do + self._droplet_id = droplet_id + self.data = None + self._state = None + + self.update() + + @property + def name(self): + """Return the name of the switch.""" + return self.data.name + + @property + def is_on(self): + """Return true if switch is on.""" + return self.data.status == 'active' + + @property + def state_attributes(self): + """Return the state attributes of the Digital Ocean droplet.""" + return { + ATTR_CREATED_AT: self.data.created_at, + ATTR_DROPLET_ID: self.data.id, + ATTR_DROPLET_NAME: self.data.name, + ATTR_FEATURES: self.data.features, + ATTR_IPV4_ADDRESS: self.data.ip_address, + ATTR_IPV6_ADDRESS: self.data.ip_v6_address, + ATTR_MEMORY: self.data.memory, + ATTR_REGION: self.data.region['name'], + ATTR_VCPUS: self.data.vcpus, + } + + def turn_on(self, **kwargs): + """Boot-up the droplet.""" + if self.data.status != 'active': + self.data.power_on() + + def turn_off(self, **kwargs): + """Shutdown the droplet.""" + if self.data.status == 'active': + self.data.power_off() + + def update(self): + """Get the latest data from the device and update the data.""" + self._digital_ocean.update() + + for droplet in self._digital_ocean.data: + if droplet.id == self._droplet_id: + self.data = droplet diff --git a/requirements_all.txt b/requirements_all.txt index 640931ceb89..21e387ab008 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -362,6 +362,9 @@ pyserial==3.1.1 # homeassistant.components.sensor.snmp pysnmp==4.3.2 +# homeassistant.components.digital_ocean +python-digitalocean==1.9.0 + # homeassistant.components.sensor.forecast python-forecastio==1.3.4