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:
parent
c78e6c088e
commit
b6d75e6c5a
3 changed files with 230 additions and 0 deletions
226
homeassistant/components/switch/netio.py
Normal file
226
homeassistant/components/switch/netio.py
Normal 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]
|
Loading…
Add table
Add a link
Reference in a new issue