Netio Switch platform support (#2181)

* WSGI based request handler

with a bit of polishing

Signed-off-by: eagleamon <joseph.piron@gmail.com>

* removed stale comment and fixed version, but failed tests do not seem to be related

* removing the wrapper hack

* added in requirements file

* Found the caved in lint error..
This commit is contained in:
Joseph Piron 2016-06-10 08:40:14 +02:00 committed by Paulus Schoutsen
parent c78e6c088e
commit b6d75e6c5a
3 changed files with 230 additions and 0 deletions

View File

@ -199,6 +199,7 @@ omit =
homeassistant/components/switch/edimax.py
homeassistant/components/switch/hikvisioncam.py
homeassistant/components/switch/mystrom.py
homeassistant/components/switch/netio.py
homeassistant/components/switch/orvibo.py
homeassistant/components/switch/pulseaudio_loopback.py
homeassistant/components/switch/rest.py

View File

@ -0,0 +1,226 @@
"""
Netio switch component.
The Netio platform allows you to control your [Netio]
(http://www.netio-products.com/en/overview/) Netio4, Netio4 All and Netio 230B.
These are smart outlets controllable through ethernet and/or WiFi that reports
consumptions (Netio4all).
To use these devices in your installation, add the following to your
configuration.yaml file:
```
switch:
- platform: netio
host: netio-living
outlets:
1: "AppleTV"
2: "Htpc"
3: "Lampe Gauche"
4: "Lampe Droite"
- platform: netio
host: 192.168.1.43
port: 1234
username: user
password: pwd
outlets:
1: "Nothing..."
4: "Lampe du fer"
```
To get pushed updates from the netio devices, one can add this lua code in the
device interface as an action triggered on "Netio" "System variables updated"
with an 'Always' schedule:
``
-- this will send socket and consumption status updates via CGI
-- to given address. Associate with 'System variables update' event
-- to get consumption updates when they show up
local address='ha:8123'
local path = '/api/netio/<host>'
local output = {}
for i = 1, 4 do for _, what in pairs({'state', 'consumption',
'cumulatedConsumption', 'consumptionStart'}) do
local varname = string.format('output%d_%s', i, what)
table.insert(output,
varname..'='..tostring(devices.system[varname]):gsub(" ","|"))
end end
local qs = table.concat(output, '&')
local url = string.format('http://%s%s?%s', address, path, qs)
devices.system.CustomCGI{url=url}
```
For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/switch.netio/
"""
import logging
from collections import namedtuple
from datetime import timedelta
from homeassistant import util
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_USERNAME, \
CONF_PASSWORD, EVENT_HOMEASSISTANT_STOP, STATE_ON
from homeassistant.helpers import validate_config
from homeassistant.components.switch import SwitchDevice
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['http']
REQUIREMENTS = ['pynetio==0.1.6']
DEFAULT_USERNAME = 'admin'
DEFAULT_PORT = 1234
URL_API_NETIO_EP = "/api/netio/<host>"
CONF_OUTLETS = "outlets"
REQ_CONF = [CONF_HOST, CONF_OUTLETS]
ATTR_TODAY_MWH = "today_mwh"
ATTR_TOTAL_CONSUMPTION_KWH = "total_energy_kwh"
ATTR_CURRENT_POWER_MWH = "current_power_mwh"
ATTR_CURRENT_POWER_W = "current_power_w"
Device = namedtuple('device', ['netio', 'entities'])
DEVICES = {}
ATTR_START_DATE = 'start_date'
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=10)
def setup_platform(hass, config, add_devices_callback, discovery_info=None):
"""Configure the netio platform."""
from pynetio import Netio
if validate_config({"conf": config}, {"conf": [CONF_OUTLETS,
CONF_HOST]}, _LOGGER):
if len(DEVICES) == 0:
hass.wsgi.register_view(NetioApiView)
dev = Netio(config[CONF_HOST],
config.get(CONF_PORT, DEFAULT_PORT),
config.get(CONF_USERNAME, DEFAULT_USERNAME),
config.get(CONF_PASSWORD, DEFAULT_USERNAME))
DEVICES[config[CONF_HOST]] = Device(dev, [])
# Throttle the update for all NetioSwitches of one Netio
dev.update = util.Throttle(MIN_TIME_BETWEEN_SCANS)(dev.update)
for key in config[CONF_OUTLETS]:
switch = NetioSwitch(DEVICES[config[CONF_HOST]].netio, key,
config[CONF_OUTLETS][key])
DEVICES[config[CONF_HOST]].entities.append(switch)
add_devices_callback(DEVICES[config[CONF_HOST]].entities)
hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, dispose)
return True
def dispose(event):
"""Close connections to Netio Devices."""
for _, value in DEVICES.items():
value.netio.stop()
class NetioApiView(HomeAssistantView):
"""WSGI handler class."""
url = URL_API_NETIO_EP
name = "api:netio"
def get(self, request, host):
"""Request handler."""
data = request.args
states, consumptions, cumulated_consumptions, start_dates = \
[], [], [], []
for i in range(1, 5):
out = 'output%d' % i
states.append(data.get('%s_state' % out) == STATE_ON)
consumptions.append(float(data.get('%s_consumption' % out, 0)))
cumulated_consumptions.append(
float(data.get('%s_cumulatedConsumption' % out, 0)) / 1000)
start_dates.append(data.get('%s_consumptionStart' % out, ""))
_LOGGER.debug('%s: %s, %s, %s since %s', host, states,
consumptions, cumulated_consumptions, start_dates)
ndev = DEVICES[host].netio
ndev.consumptions = consumptions
ndev.cumulated_consumptions = cumulated_consumptions
ndev.states = states
ndev.start_dates = start_dates
for dev in DEVICES[host].entities:
dev.update_ha_state()
return self.json(True)
class NetioSwitch(SwitchDevice):
"""Provide a netio linked switch."""
def __init__(self, netio, outlet, name):
"""Defined to handle throttle."""
self._name = name
self.outlet = outlet
self.netio = netio
@property
def name(self):
"""Netio device's name."""
return self._name
@property
def available(self):
"""Return True if entity is available."""
return not hasattr(self, 'telnet')
def turn_on(self):
"""Turn switch on."""
self._set(True)
def turn_off(self):
"""Turn switch off."""
self._set(False)
def _set(self, value):
val = list('uuuu')
val[self.outlet - 1] = "1" if value else "0"
self.netio.get('port list %s' % ''.join(val))
self.netio.states[self.outlet - 1] = value
self.update_ha_state()
@property
def is_on(self):
"""Return switch's status."""
return self.netio.states[self.outlet - 1]
def update(self):
"""Called by HA."""
self.netio.update()
@property
def state_attributes(self):
"""Return optional state attributes."""
return {ATTR_CURRENT_POWER_W: self.current_power_w,
ATTR_TOTAL_CONSUMPTION_KWH: self.cumulated_consumption_kwh,
ATTR_START_DATE: self.start_date.split('|')[0]}
@property
def current_power_w(self):
"""Return actual power."""
return self.netio.consumptions[self.outlet - 1]
@property
def cumulated_consumption_kwh(self):
"""Total enerygy consumption since start_date."""
return self.netio.cumulated_consumptions[self.outlet - 1]
@property
def start_date(self):
"""Point in time when the energy accumulation started."""
return self.netio.start_dates[self.outlet - 1]

View File

@ -251,6 +251,9 @@ pyloopenergy==0.0.13
# homeassistant.components.device_tracker.netgear
pynetgear==0.3.3
# homeassistant.components.switch.netio
pynetio==0.1.6
# homeassistant.components.alarm_control_panel.nx584
# homeassistant.components.binary_sensor.nx584
pynx584==0.2