mirror of
https://github.com/home-assistant/core.git
synced 2025-07-20 19:57:07 +00:00
Insteon local (#5088)
* platform set-up begin components * lights seem to be getting set up properly, not sure why they aren't being added... * typo * Dependencies line * toggle working * toggle working * added the switch to insteon_local First commit hope to test tonight or in the morning * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * Update insteon_local.py * move dependency declaration before import? * Move dependencies in Switch * Update insteon_local.py * wait for response * switched the while to an if switched the while 'cmd2' not in resp: to an if 'cmd2' not in resp: this seems to have the updater working * Switched the while sleep loop to an if switched the wile cmd2 not ins resp to be if cmd2 not in resp seems to be working. * Update insteon_local.py * import statement Updated the import statement to import the instance of the insteon_local component not the hub Instance. * updated import and the device assignment update the import to import the instance of the insteon_local component not the hub. * more changes to support the import change * more changes to support the import change * change to hass.data and add loop logic * && * Update insteon_local.py * Update insteon_local.py * logic fixes and throttle * reduce polling time * brightness support * import util * hound fixes * requirements file * more hound fixes * newline * newline weirdness * lint fixes * more lint fixes * switch state * Update insteon_local.py * log cmd2 for debugging * assume success * remove check for none * fix comments * fix comments again * fix comments, add fixed version of lib, add support for timeout, add support for port, handle invalid login and connection problems * fix logging exception * fix hounceci-bot errors * fix hounceci-bot errors * requirements fix * unique-id changes * make dimmer off use saved ramp rate * configurator working for lights * configurator working for switches? * configurator working for switches? * include model names and fix lint errors * lint fix * fix exception order * lint fixes * fix lint errors * update to use insteon local 0.38 * fix device id * move status check to library * move status check to library * add SKU to setup * lint fixes * requirements * linting
This commit is contained in:
parent
2b14d407c0
commit
a3971d7ad1
@ -37,6 +37,9 @@ omit =
|
||||
homeassistant/components/insteon_hub.py
|
||||
homeassistant/components/*/insteon_hub.py
|
||||
|
||||
homeassistant/components/insteon_local.py
|
||||
homeassistant/components/*/insteon_local.py
|
||||
|
||||
homeassistant/components/ios.py
|
||||
homeassistant/components/*/ios.py
|
||||
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
74
homeassistant/components/insteon_local.py
Normal file
74
homeassistant/components/insteon_local.py
Normal file
@ -0,0 +1,74 @@
|
||||
"""
|
||||
Local Support for Insteon.
|
||||
|
||||
Based on the insteonlocal library
|
||||
https://github.com/phareous/insteonlocal
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/insteon_local/
|
||||
"""
|
||||
import logging
|
||||
import voluptuous as vol
|
||||
import requests
|
||||
from homeassistant.const import (CONF_PASSWORD, CONF_USERNAME, CONF_HOST,
|
||||
CONF_PORT, CONF_TIMEOUT)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
REQUIREMENTS = ['insteonlocal==0.39']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = 'insteon_local'
|
||||
|
||||
DEFAULT_PORT = 25105
|
||||
|
||||
DEFAULT_TIMEOUT = 10
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema({
|
||||
DOMAIN: vol.Schema({
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
|
||||
vol.Optional(CONF_TIMEOUT, default=DEFAULT_TIMEOUT): cv.positive_int
|
||||
})
|
||||
}, extra=vol.ALLOW_EXTRA)
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Setup Insteon Hub component.
|
||||
|
||||
This will automatically import associated lights.
|
||||
"""
|
||||
from insteonlocal.Hub import Hub
|
||||
|
||||
conf = config[DOMAIN]
|
||||
username = conf.get(CONF_USERNAME)
|
||||
password = conf.get(CONF_PASSWORD)
|
||||
host = conf.get(CONF_HOST)
|
||||
port = conf.get(CONF_PORT)
|
||||
timeout = conf.get(CONF_TIMEOUT)
|
||||
|
||||
try:
|
||||
insteonhub = Hub(host, username, password, port, timeout, _LOGGER)
|
||||
# check for successful connection
|
||||
insteonhub.get_buffer_status()
|
||||
except requests.exceptions.ConnectTimeout:
|
||||
_LOGGER.error("Error on insteon_local."
|
||||
"Could not connect. Check config", exc_info=True)
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
_LOGGER.error("Error on insteon_local. Could not connect."
|
||||
"Check config", exc_info=True)
|
||||
return False
|
||||
except requests.exceptions.RequestException:
|
||||
if insteonhub.http_code == 401:
|
||||
_LOGGER.error("Bad user/pass for insteon_local hub")
|
||||
return False
|
||||
else:
|
||||
_LOGGER.error("Error on insteon_local hub check", exc_info=True)
|
||||
return False
|
||||
|
||||
hass.data['insteon_local'] = insteonhub
|
||||
|
||||
return True
|
191
homeassistant/components/light/insteon_local.py
Normal file
191
homeassistant/components/light/insteon_local.py
Normal file
@ -0,0 +1,191 @@
|
||||
"""
|
||||
Support for Insteon dimmers via local hub control.
|
||||
|
||||
Based on the insteonlocal library
|
||||
https://github.com/phareous/insteonlocal
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/light.insteon_local/
|
||||
|
||||
--
|
||||
Example platform config
|
||||
--
|
||||
|
||||
insteon_local:
|
||||
host: YOUR HUB IP
|
||||
username: YOUR HUB USERNAME
|
||||
password: YOUR HUB PASSWORD
|
||||
timeout: 10
|
||||
port: 25105
|
||||
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from homeassistant.components.light import (ATTR_BRIGHTNESS,
|
||||
SUPPORT_BRIGHTNESS, Light)
|
||||
from homeassistant.loader import get_component
|
||||
import homeassistant.util as util
|
||||
|
||||
INSTEON_LOCAL_LIGHTS_CONF = 'insteon_local_lights.conf'
|
||||
|
||||
DEPENDENCIES = ['insteon_local']
|
||||
|
||||
SUPPORT_INSTEON_LOCAL = SUPPORT_BRIGHTNESS
|
||||
|
||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
|
||||
|
||||
DOMAIN = "light"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_CONFIGURING = {}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Insteon local light platform."""
|
||||
insteonhub = hass.data['insteon_local']
|
||||
|
||||
conf_lights = config_from_file(hass.config.path(INSTEON_LOCAL_LIGHTS_CONF))
|
||||
if len(conf_lights):
|
||||
for device_id in conf_lights:
|
||||
setup_light(device_id, conf_lights[device_id], insteonhub, hass,
|
||||
add_devices)
|
||||
|
||||
linked = insteonhub.get_linked()
|
||||
|
||||
for device_id in linked:
|
||||
if (linked[device_id]['cat_type'] == 'dimmer' and
|
||||
device_id not in conf_lights):
|
||||
request_configuration(device_id,
|
||||
insteonhub,
|
||||
linked[device_id]['model_name'] + ' ' +
|
||||
linked[device_id]['sku'], hass, add_devices)
|
||||
|
||||
|
||||
def request_configuration(device_id, insteonhub, model, hass,
|
||||
add_devices_callback):
|
||||
"""Request configuration steps from the user."""
|
||||
configurator = get_component('configurator')
|
||||
|
||||
# We got an error if this method is called while we are configuring
|
||||
if device_id in _CONFIGURING:
|
||||
configurator.notify_errors(
|
||||
_CONFIGURING[device_id], 'Failed to register, please try again.')
|
||||
|
||||
return
|
||||
|
||||
def insteon_light_config_callback(data):
|
||||
"""The actions to do when our configuration callback is called."""
|
||||
setup_light(device_id, data.get('name'), insteonhub, hass,
|
||||
add_devices_callback)
|
||||
|
||||
_CONFIGURING[device_id] = configurator.request_config(
|
||||
hass, 'Insteon ' + model + ' addr: ' + device_id,
|
||||
insteon_light_config_callback,
|
||||
description=('Enter a name for ' + model + ' addr: ' + device_id),
|
||||
entity_picture='/static/images/config_insteon.png',
|
||||
submit_caption='Confirm',
|
||||
fields=[{'id': 'name', 'name': 'Name', 'type': ''}]
|
||||
)
|
||||
|
||||
|
||||
def setup_light(device_id, name, insteonhub, hass, add_devices_callback):
|
||||
"""Setup light."""
|
||||
if device_id in _CONFIGURING:
|
||||
request_id = _CONFIGURING.pop(device_id)
|
||||
configurator = get_component('configurator')
|
||||
configurator.request_done(request_id)
|
||||
_LOGGER.info('Device configuration done!')
|
||||
|
||||
conf_lights = config_from_file(hass.config.path(INSTEON_LOCAL_LIGHTS_CONF))
|
||||
if device_id not in conf_lights:
|
||||
conf_lights[device_id] = name
|
||||
|
||||
if not config_from_file(
|
||||
hass.config.path(INSTEON_LOCAL_LIGHTS_CONF),
|
||||
conf_lights):
|
||||
_LOGGER.error('failed to save config file')
|
||||
|
||||
device = insteonhub.dimmer(device_id)
|
||||
add_devices_callback([InsteonLocalDimmerDevice(device, name)])
|
||||
|
||||
|
||||
def config_from_file(filename, config=None):
|
||||
"""Small configuration file management function."""
|
||||
if config:
|
||||
# We're writing configuration
|
||||
try:
|
||||
with open(filename, 'w') as fdesc:
|
||||
fdesc.write(json.dumps(config))
|
||||
except IOError as error:
|
||||
_LOGGER.error('Saving config file failed: %s', error)
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
# We're reading config
|
||||
if os.path.isfile(filename):
|
||||
try:
|
||||
with open(filename, 'r') as fdesc:
|
||||
return json.loads(fdesc.read())
|
||||
except IOError as error:
|
||||
_LOGGER.error('Reading config file failed: %s', error)
|
||||
# This won't work yet
|
||||
return False
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
class InsteonLocalDimmerDevice(Light):
|
||||
"""An abstract Class for an Insteon node."""
|
||||
|
||||
def __init__(self, node, name):
|
||||
"""Initialize the device."""
|
||||
self.node = node
|
||||
self.node.deviceName = name
|
||||
self._value = 0
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
return self.node.deviceName
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the ID of this insteon node."""
|
||||
return 'insteon_local_' + self.node.device_id
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return self._value
|
||||
|
||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
|
||||
def update(self):
|
||||
"""Update state of the light."""
|
||||
resp = self.node.status(0)
|
||||
if 'cmd2' in resp:
|
||||
self._value = int(resp['cmd2'], 16)
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return the boolean response if the node is on."""
|
||||
return self._value != 0
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
return SUPPORT_INSTEON_LOCAL
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn device on."""
|
||||
brightness = 100
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
brightness = int(kwargs[ATTR_BRIGHTNESS]) / 255 * 100
|
||||
|
||||
self.node.on(brightness)
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn device off."""
|
||||
self.node.off()
|
176
homeassistant/components/switch/insteon_local.py
Normal file
176
homeassistant/components/switch/insteon_local.py
Normal file
@ -0,0 +1,176 @@
|
||||
"""
|
||||
Support for Insteon switch devices via local hub support.
|
||||
|
||||
Based on the insteonlocal library
|
||||
https://github.com/phareous/insteonlocal
|
||||
|
||||
For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/switch.insteon_local/
|
||||
|
||||
--
|
||||
Example platform config
|
||||
--
|
||||
|
||||
insteon_local:
|
||||
host: YOUR HUB IP
|
||||
username: YOUR HUB USERNAME
|
||||
password: YOUR HUB PASSWORD
|
||||
timeout: 10
|
||||
port: 25105
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from datetime import timedelta
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
from homeassistant.loader import get_component
|
||||
import homeassistant.util as util
|
||||
|
||||
INSTEON_LOCAL_SWITCH_CONF = 'insteon_local_switch.conf'
|
||||
|
||||
DEPENDENCIES = ['insteon_local']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MIN_TIME_BETWEEN_SCANS = timedelta(seconds=5)
|
||||
MIN_TIME_BETWEEN_FORCED_SCANS = timedelta(milliseconds=100)
|
||||
|
||||
DOMAIN = "switch"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_CONFIGURING = {}
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Insteon local switch platform."""
|
||||
insteonhub = hass.data['insteon_local']
|
||||
|
||||
conf_switches = config_from_file(hass.config.path(
|
||||
INSTEON_LOCAL_SWITCH_CONF))
|
||||
if len(conf_switches):
|
||||
for device_id in conf_switches:
|
||||
setup_switch(device_id, conf_switches[device_id], insteonhub,
|
||||
hass, add_devices)
|
||||
|
||||
linked = insteonhub.get_inked()
|
||||
|
||||
for device_id in linked:
|
||||
if linked[device_id]['cat_type'] == 'switch'\
|
||||
and device_id not in conf_switches:
|
||||
request_configuration(device_id, insteonhub,
|
||||
linked[device_id]['model_name'] + ' ' +
|
||||
linked[device_id]['sku'], hass, add_devices)
|
||||
|
||||
|
||||
def request_configuration(device_id, insteonhub, model, hass,
|
||||
add_devices_callback):
|
||||
"""Request configuration steps from the user."""
|
||||
configurator = get_component('configurator')
|
||||
|
||||
# We got an error if this method is called while we are configuring
|
||||
if device_id in _CONFIGURING:
|
||||
configurator.notify_errors(
|
||||
_CONFIGURING[device_id], 'Failed to register, please try again.')
|
||||
|
||||
return
|
||||
|
||||
def insteon_switch_config_callback(data):
|
||||
"""The actions to do when our configuration callback is called."""
|
||||
setup_switch(device_id, data.get('name'), insteonhub, hass,
|
||||
add_devices_callback)
|
||||
|
||||
_CONFIGURING[device_id] = configurator.request_config(
|
||||
hass, 'Insteon Switch ' + model + ' addr: ' + device_id,
|
||||
insteon_switch_config_callback,
|
||||
description=('Enter a name for ' + model + ' addr: ' + device_id),
|
||||
entity_picture='/static/images/config_insteon.png',
|
||||
submit_caption='Confirm',
|
||||
fields=[{'id': 'name', 'name': 'Name', 'type': ''}]
|
||||
)
|
||||
|
||||
|
||||
def setup_switch(device_id, name, insteonhub, hass, add_devices_callback):
|
||||
"""Setup switch."""
|
||||
if device_id in _CONFIGURING:
|
||||
request_id = _CONFIGURING.pop(device_id)
|
||||
configurator = get_component('configurator')
|
||||
configurator.request_done(request_id)
|
||||
_LOGGER.info('Device configuration done!')
|
||||
|
||||
conf_switch = config_from_file(hass.config.path(INSTEON_LOCAL_SWITCH_CONF))
|
||||
if device_id not in conf_switch:
|
||||
conf_switch[device_id] = name
|
||||
|
||||
if not config_from_file(
|
||||
hass.config.path(INSTEON_LOCAL_SWITCH_CONF), conf_switch):
|
||||
_LOGGER.error('failed to save config file')
|
||||
|
||||
device = insteonhub.switch(device_id)
|
||||
add_devices_callback([InsteonLocalSwitchDevice(device, name)])
|
||||
|
||||
|
||||
def config_from_file(filename, config=None):
|
||||
"""Small configuration file management function."""
|
||||
if config:
|
||||
# We're writing configuration
|
||||
try:
|
||||
with open(filename, 'w') as fdesc:
|
||||
fdesc.write(json.dumps(config))
|
||||
except IOError as error:
|
||||
_LOGGER.error('Saving config file failed: %s', error)
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
# We're reading config
|
||||
if os.path.isfile(filename):
|
||||
try:
|
||||
with open(filename, 'r') as fdesc:
|
||||
return json.loads(fdesc.read())
|
||||
except IOError as error:
|
||||
_LOGGER.error('Reading config file failed: %s', error)
|
||||
# This won't work yet
|
||||
return False
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
class InsteonLocalSwitchDevice(SwitchDevice):
|
||||
"""An abstract Class for an Insteon node."""
|
||||
|
||||
def __init__(self, node, name):
|
||||
"""Initialize the device."""
|
||||
self.node = node
|
||||
self.node.deviceName = name
|
||||
self._state = False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the the name of the node."""
|
||||
return self.node.deviceName
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return the ID of this insteon node."""
|
||||
return 'insteon_local_' + self.node.device_id
|
||||
|
||||
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
|
||||
def update(self):
|
||||
"""Get the updated status of the switch."""
|
||||
resp = self.node.status(0)
|
||||
if 'cmd2' in resp:
|
||||
self._state = int(resp['cmd2'], 16) > 0
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return the boolean response if the node is on."""
|
||||
return self._state
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn device on."""
|
||||
self.node.on()
|
||||
self._state = True
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn device off."""
|
||||
self.node.off()
|
||||
self._state = False
|
@ -271,6 +271,9 @@ influxdb==3.0.0
|
||||
# homeassistant.components.insteon_hub
|
||||
insteon_hub==0.4.5
|
||||
|
||||
# homeassistant.components.insteon_local
|
||||
insteonlocal==0.39
|
||||
|
||||
# homeassistant.components.media_player.kodi
|
||||
jsonrpc-async==0.1
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user