Ported codebase to Python 3. Long Live Python 3!
This commit is contained in:
parent
8fdf2d608a
commit
7e06d535ab
14 changed files with 105 additions and 73 deletions
|
@ -1,7 +1,7 @@
|
||||||
Home Assistant
|
Home Assistant
|
||||||
==============
|
==============
|
||||||
|
|
||||||
Home Assistant provides a platform for home automation. It does so by having modules that observe and trigger actors to do various tasks.
|
Home Assistant is a home automation platform running on Python 3. It provides modules that observe and trigger actors to do various tasks.
|
||||||
|
|
||||||
It is currently able to do the following things:
|
It is currently able to do the following things:
|
||||||
* Track if devices are home by monitoring connected devices to a wireless router (currently supporting modern Netgear routers or routers running Tomato firmware)
|
* Track if devices are home by monitoring connected devices to a wireless router (currently supporting modern Netgear routers or routers running Tomato firmware)
|
||||||
|
|
|
@ -171,7 +171,7 @@ def create_bus_job_handler(logger):
|
||||||
except Exception: # pylint: disable=broad-except
|
except Exception: # pylint: disable=broad-except
|
||||||
# Catch any exception our service/event_listener might throw
|
# Catch any exception our service/event_listener might throw
|
||||||
# We do not want to crash our ThreadPool
|
# We do not want to crash our ThreadPool
|
||||||
logger.exception(u"BusHandler:Exception doing job")
|
logger.exception("BusHandler:Exception doing job")
|
||||||
|
|
||||||
return job_handler
|
return job_handler
|
||||||
|
|
||||||
|
@ -189,10 +189,10 @@ class ServiceCall(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.data:
|
if self.data:
|
||||||
return u"<ServiceCall {}.{}: {}>".format(
|
return "<ServiceCall {}.{}: {}>".format(
|
||||||
self.domain, self.service, util.repr_helper(self.data))
|
self.domain, self.service, util.repr_helper(self.data))
|
||||||
else:
|
else:
|
||||||
return u"<ServiceCall {}.{}>".format(self.domain, self.service)
|
return "<ServiceCall {}.{}>".format(self.domain, self.service)
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
|
@ -207,10 +207,10 @@ class Event(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.data:
|
if self.data:
|
||||||
return u"<Event {}: {}>".format(
|
return "<Event {}: {}>".format(
|
||||||
self.event_type, util.repr_helper(self.data))
|
self.event_type, util.repr_helper(self.data))
|
||||||
else:
|
else:
|
||||||
return u"<Event {}>".format(self.event_type)
|
return "<Event {}>".format(self.event_type)
|
||||||
|
|
||||||
|
|
||||||
class Bus(object):
|
class Bus(object):
|
||||||
|
@ -235,7 +235,7 @@ class Bus(object):
|
||||||
def services(self):
|
def services(self):
|
||||||
""" Dict with per domain a list of available services. """
|
""" Dict with per domain a list of available services. """
|
||||||
with self.service_lock:
|
with self.service_lock:
|
||||||
return {domain: self._services[domain].keys()
|
return {domain: list(self._services[domain].keys())
|
||||||
for domain in self._services}
|
for domain in self._services}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -268,7 +268,7 @@ class Bus(object):
|
||||||
|
|
||||||
except KeyError: # if key domain or service does not exist
|
except KeyError: # if key domain or service does not exist
|
||||||
raise ServiceDoesNotExistError(
|
raise ServiceDoesNotExistError(
|
||||||
u"Service does not exist: {}/{}".format(domain, service))
|
"Service does not exist: {}/{}".format(domain, service))
|
||||||
|
|
||||||
def register_service(self, domain, service, service_func):
|
def register_service(self, domain, service, service_func):
|
||||||
""" Register a service. """
|
""" Register a service. """
|
||||||
|
@ -290,7 +290,7 @@ class Bus(object):
|
||||||
|
|
||||||
event = Event(event_type, event_data)
|
event = Event(event_type, event_data)
|
||||||
|
|
||||||
self.logger.info(u"Bus:Handling {}".format(event))
|
self.logger.info("Bus:Handling {}".format(event))
|
||||||
|
|
||||||
if not listeners:
|
if not listeners:
|
||||||
return
|
return
|
||||||
|
@ -363,7 +363,7 @@ class Bus(object):
|
||||||
def _check_busy(self):
|
def _check_busy(self):
|
||||||
""" Complain if we have more than twice as many jobs queued as threads
|
""" Complain if we have more than twice as many jobs queued as threads
|
||||||
and if we didn't complain about it recently. """
|
and if we didn't complain about it recently. """
|
||||||
if self.pool.queue.qsize() / self.thread_count >= 2 and \
|
if self.pool.work_queue.qsize() / self.thread_count >= 2 and \
|
||||||
dt.datetime.now()-self.last_busy_notice > BUS_REPORT_BUSY_TIMEOUT:
|
dt.datetime.now()-self.last_busy_notice > BUS_REPORT_BUSY_TIMEOUT:
|
||||||
|
|
||||||
self.last_busy_notice = dt.datetime.now()
|
self.last_busy_notice = dt.datetime.now()
|
||||||
|
@ -371,13 +371,13 @@ class Bus(object):
|
||||||
log_error = self.logger.error
|
log_error = self.logger.error
|
||||||
|
|
||||||
log_error(
|
log_error(
|
||||||
u"Bus:All {} threads are busy and {} jobs pending".format(
|
"Bus:All {} threads are busy and {} jobs pending".format(
|
||||||
self.thread_count, self.pool.queue.qsize()))
|
self.thread_count, self.pool.work_queue.qsize()))
|
||||||
|
|
||||||
jobs = self.pool.current_jobs
|
jobs = self.pool.current_jobs
|
||||||
|
|
||||||
for start, job in jobs:
|
for start, job in jobs:
|
||||||
log_error(u"Bus:Current job from {}: {}".format(
|
log_error("Bus:Current job from {}: {}".format(
|
||||||
util.datetime_to_str(start), job))
|
util.datetime_to_str(start), job))
|
||||||
|
|
||||||
|
|
||||||
|
@ -436,11 +436,11 @@ class State(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.attributes:
|
if self.attributes:
|
||||||
return u"<state {}:{} @ {}>".format(
|
return "<state {}:{} @ {}>".format(
|
||||||
self.state, util.repr_helper(self.attributes),
|
self.state, util.repr_helper(self.attributes),
|
||||||
util.datetime_to_str(self.last_changed))
|
util.datetime_to_str(self.last_changed))
|
||||||
else:
|
else:
|
||||||
return u"<state {} @ {}>".format(
|
return "<state {} @ {}>".format(
|
||||||
self.state, util.datetime_to_str(self.last_changed))
|
self.state, util.datetime_to_str(self.last_changed))
|
||||||
|
|
||||||
|
|
||||||
|
@ -456,7 +456,7 @@ class StateMachine(object):
|
||||||
def entity_ids(self):
|
def entity_ids(self):
|
||||||
""" List of entitie ids that are being tracked. """
|
""" List of entitie ids that are being tracked. """
|
||||||
with self.lock:
|
with self.lock:
|
||||||
return self.states.keys()
|
return list(self.states.keys())
|
||||||
|
|
||||||
def remove_entity(self, entity_id):
|
def remove_entity(self, entity_id):
|
||||||
""" Removes a entity from the state machine.
|
""" Removes a entity from the state machine.
|
||||||
|
@ -515,9 +515,9 @@ class StateMachine(object):
|
||||||
def is_state(self, entity_id, state):
|
def is_state(self, entity_id, state):
|
||||||
""" Returns True if entity exists and is specified state. """
|
""" Returns True if entity exists and is specified state. """
|
||||||
try:
|
try:
|
||||||
return self.get_state(entity_id).state == state
|
return self.states.get(entity_id).state == state
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
# get_state returned None
|
# states.get returned None
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ Provides methods to bootstrap a home assistant instance.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import importlib
|
import importlib
|
||||||
import ConfigParser
|
import configparser
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
|
@ -33,7 +33,7 @@ def from_config_file(config_path):
|
||||||
statusses = []
|
statusses = []
|
||||||
|
|
||||||
# Read config
|
# Read config
|
||||||
config = ConfigParser.SafeConfigParser()
|
config = configparser.SafeConfigParser()
|
||||||
config.read(config_path)
|
config.read(config_path)
|
||||||
|
|
||||||
# Init core
|
# Init core
|
||||||
|
@ -51,7 +51,7 @@ def from_config_file(config_path):
|
||||||
""" Failure proof option retriever. """
|
""" Failure proof option retriever. """
|
||||||
try:
|
try:
|
||||||
return config.get(section, option)
|
return config.get(section, option)
|
||||||
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
|
except (configparser.NoSectionError, configparser.NoOptionError):
|
||||||
return default
|
return default
|
||||||
|
|
||||||
# Device scanner
|
# Device scanner
|
||||||
|
@ -83,7 +83,7 @@ def from_config_file(config_path):
|
||||||
get_opt('device_tracker.netgear', 'username'),
|
get_opt('device_tracker.netgear', 'username'),
|
||||||
get_opt('device_tracker.netgear', 'password'))
|
get_opt('device_tracker.netgear', 'password'))
|
||||||
|
|
||||||
except ConfigParser.NoOptionError:
|
except configparser.NoOptionError:
|
||||||
# If one of the options didn't exist
|
# If one of the options didn't exist
|
||||||
logger.exception(("Error initializing {}DeviceScanner, "
|
logger.exception(("Error initializing {}DeviceScanner, "
|
||||||
"could not find one of the following config "
|
"could not find one of the following config "
|
||||||
|
@ -142,7 +142,11 @@ def from_config_file(config_path):
|
||||||
|
|
||||||
add_status("Light - Hue", light_control.success_init)
|
add_status("Light - Hue", light_control.success_init)
|
||||||
|
|
||||||
light.setup(bus, statemachine, light_control)
|
if light_control.success_init:
|
||||||
|
light.setup(bus, statemachine, light_control)
|
||||||
|
else:
|
||||||
|
light_control = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
light_control = None
|
light_control = None
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ def setup(bus, statemachine):
|
||||||
entity_id = util.ensure_unique_string(
|
entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(
|
ENTITY_ID_FORMAT.format(
|
||||||
util.slugify(cast.device.friendly_name)),
|
util.slugify(cast.device.friendly_name)),
|
||||||
casts.keys())
|
list(casts.keys()))
|
||||||
|
|
||||||
casts[entity_id] = cast
|
casts[entity_id] = cast
|
||||||
|
|
||||||
|
@ -189,8 +189,7 @@ def setup(bus, statemachine):
|
||||||
yield entity_id, cast
|
yield entity_id, cast
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for item in casts.items():
|
yield from casts.items()
|
||||||
yield item
|
|
||||||
|
|
||||||
def turn_off_service(service):
|
def turn_off_service(service):
|
||||||
""" Service to exit any running app on the specified ChromeCast and
|
""" Service to exit any running app on the specified ChromeCast and
|
||||||
|
@ -230,7 +229,7 @@ def setup(bus, statemachine):
|
||||||
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
|
ramp = cast.get_protocol(pychromecast.PROTOCOL_RAMP)
|
||||||
|
|
||||||
if ramp:
|
if ramp:
|
||||||
ramp.next()
|
next(ramp)
|
||||||
update_chromecast_state(entity_id, cast)
|
update_chromecast_state(entity_id, cast)
|
||||||
|
|
||||||
def play_youtube_video_service(service, video_id):
|
def play_youtube_video_service(service, video_id):
|
||||||
|
|
|
@ -51,7 +51,7 @@ def setup(bus, statemachine,
|
||||||
next_setting = sun.next_setting(statemachine)
|
next_setting = sun.next_setting(statemachine)
|
||||||
|
|
||||||
if next_setting:
|
if next_setting:
|
||||||
return (next_setting - LIGHT_TRANSITION_TIME * len(light_ids))
|
return next_setting - LIGHT_TRANSITION_TIME * len(light_ids)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ class DeviceTracker(object):
|
||||||
# Because we do not want to have stuff happening when the device does
|
# Because we do not want to have stuff happening when the device does
|
||||||
# not show up for 1 scan beacuse of reboot etc
|
# not show up for 1 scan beacuse of reboot etc
|
||||||
for device in temp_tracking_devices:
|
for device in temp_tracking_devices:
|
||||||
if (now - known_dev[device]['last_seen'] > self.error_scanning):
|
if now - known_dev[device]['last_seen'] > self.error_scanning:
|
||||||
|
|
||||||
self.statemachine.set_state(known_dev[device]['entity_id'],
|
self.statemachine.set_state(known_dev[device]['entity_id'],
|
||||||
components.STATE_NOT_HOME)
|
components.STATE_NOT_HOME)
|
||||||
|
|
|
@ -72,8 +72,8 @@ import threading
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
from urlparse import urlparse, parse_qs
|
from urllib.parse import urlparse, parse_qs
|
||||||
|
|
||||||
import homeassistant as ha
|
import homeassistant as ha
|
||||||
import homeassistant.util as util
|
import homeassistant.util as util
|
||||||
|
@ -138,6 +138,7 @@ class HTTPInterface(threading.Thread):
|
||||||
self.server.serve_forever()
|
self.server.serve_forever()
|
||||||
|
|
||||||
|
|
||||||
|
# pylint: disable=too-many-public-methods
|
||||||
class RequestHandler(BaseHTTPRequestHandler):
|
class RequestHandler(BaseHTTPRequestHandler):
|
||||||
""" Handles incoming HTTP requests """
|
""" Handles incoming HTTP requests """
|
||||||
|
|
||||||
|
@ -188,7 +189,8 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
content_length = int(self.headers.get('Content-Length', 0))
|
content_length = int(self.headers.get('Content-Length', 0))
|
||||||
|
|
||||||
if content_length:
|
if content_length:
|
||||||
data.update(parse_qs(self.rfile.read(content_length)))
|
data.update(parse_qs(self.rfile.read(
|
||||||
|
content_length).decode("UTF-8")))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
api_password = data['api_password'][0]
|
api_password = data['api_password'][0]
|
||||||
|
@ -282,7 +284,7 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
"</form>"
|
"</form>"
|
||||||
"</div>"
|
"</div>"
|
||||||
"</body></html>").format(self.path))
|
"</body></html>").format(self.path).encode("UTF-8"))
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -290,7 +292,7 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
def _handle_get_root(self, path_match, data):
|
def _handle_get_root(self, path_match, data):
|
||||||
""" Renders the debug interface. """
|
""" Renders the debug interface. """
|
||||||
|
|
||||||
write = lambda txt: self.wfile.write(txt.encode("UTF-8")+"\n")
|
write = lambda txt: self.wfile.write((txt + "\n").encode("UTF-8"))
|
||||||
|
|
||||||
self.send_response(HTTP_OK)
|
self.send_response(HTTP_OK)
|
||||||
self.send_header('Content-type', 'text/html; charset=utf-8')
|
self.send_header('Content-type', 'text/html; charset=utf-8')
|
||||||
|
@ -335,13 +337,13 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
state = self.server.statemachine.get_state(entity_id)
|
state = self.server.statemachine.get_state(entity_id)
|
||||||
|
|
||||||
attributes = u"<br>".join(
|
attributes = "<br>".join(
|
||||||
[u"{}: {}".format(attr, state.attributes[attr])
|
["{}: {}".format(attr, state.attributes[attr])
|
||||||
for attr in state.attributes])
|
for attr in state.attributes])
|
||||||
|
|
||||||
write((u"<tr>"
|
write(("<tr>"
|
||||||
u"<td>{}</td><td>{}</td><td>{}</td><td>{}</td>"
|
"<td>{}</td><td>{}</td><td>{}</td><td>{}</td>"
|
||||||
u"</tr>").format(
|
"</tr>").format(
|
||||||
entity_id,
|
entity_id,
|
||||||
state.state,
|
state.state,
|
||||||
attributes,
|
attributes,
|
||||||
|
@ -686,4 +688,5 @@ class RequestHandler(BaseHTTPRequestHandler):
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
self.wfile.write(json.dumps(data, indent=4, sort_keys=True))
|
self.wfile.write(
|
||||||
|
json.dumps(data, indent=4, sort_keys=True).encode("UTF-8"))
|
||||||
|
|
|
@ -155,11 +155,11 @@ def setup(bus, statemachine, light_control):
|
||||||
# We have not seen this light before, set it up
|
# We have not seen this light before, set it up
|
||||||
|
|
||||||
# Create entity id
|
# Create entity id
|
||||||
logger.info(u"Found new light {}".format(name))
|
logger.info("Found new light {}".format(name))
|
||||||
|
|
||||||
entity_id = util.ensure_unique_string(
|
entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
ENTITY_ID_FORMAT.format(util.slugify(name)),
|
||||||
ent_to_light.keys())
|
list(ent_to_light.keys()))
|
||||||
|
|
||||||
ent_to_light[entity_id] = light_id
|
ent_to_light[entity_id] = light_id
|
||||||
light_to_ent[light_id] = entity_id
|
light_to_ent[light_id] = entity_id
|
||||||
|
@ -218,7 +218,7 @@ def setup(bus, statemachine, light_control):
|
||||||
file_path = os.path.join(dir_path, LIGHT_PROFILES_FILE)
|
file_path = os.path.join(dir_path, LIGHT_PROFILES_FILE)
|
||||||
|
|
||||||
if os.path.isfile(file_path):
|
if os.path.isfile(file_path):
|
||||||
with open(file_path, 'rb') as inp:
|
with open(file_path) as inp:
|
||||||
reader = csv.reader(inp)
|
reader = csv.reader(inp)
|
||||||
|
|
||||||
# Skip the header
|
# Skip the header
|
||||||
|
@ -249,7 +249,7 @@ def setup(bus, statemachine, light_control):
|
||||||
if entity_id in ent_to_light]
|
if entity_id in ent_to_light]
|
||||||
|
|
||||||
if not light_ids:
|
if not light_ids:
|
||||||
light_ids = ent_to_light.values()
|
light_ids = list(ent_to_light.values())
|
||||||
|
|
||||||
transition = util.convert(dat.get(ATTR_TRANSITION), int)
|
transition = util.convert(dat.get(ATTR_TRANSITION), int)
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ def setup(bus, statemachine):
|
||||||
# New device, set it up
|
# New device, set it up
|
||||||
entity_id = util.ensure_unique_string(
|
entity_id = util.ensure_unique_string(
|
||||||
ENTITY_ID_FORMAT.format(util.slugify(device.name)),
|
ENTITY_ID_FORMAT.format(util.slugify(device.name)),
|
||||||
ent_to_dev.keys())
|
list(ent_to_dev.keys()))
|
||||||
|
|
||||||
sno_to_ent[device.serialnumber] = entity_id
|
sno_to_ent[device.serialnumber] = entity_id
|
||||||
ent_to_dev[entity_id] = device
|
ent_to_dev[entity_id] = device
|
||||||
|
@ -115,7 +115,7 @@ def setup(bus, statemachine):
|
||||||
|
|
||||||
# Track all lights in a group
|
# Track all lights in a group
|
||||||
group.setup(bus, statemachine,
|
group.setup(bus, statemachine,
|
||||||
GROUP_NAME_ALL_WEMOS, sno_to_ent.values())
|
GROUP_NAME_ALL_WEMOS, list(sno_to_ent.values()))
|
||||||
|
|
||||||
def _handle_wemo_service(service):
|
def _handle_wemo_service(service):
|
||||||
""" Handles calls to the WeMo service. """
|
""" Handles calls to the WeMo service. """
|
||||||
|
|
2
homeassistant/external/pynetgear
vendored
2
homeassistant/external/pynetgear
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 9e094c322890bbcdd6cf4b0af4fd763f227cd64f
|
Subproject commit bc635995789fc91c9b4a2fecaeb50241ab4fbf2a
|
|
@ -12,7 +12,7 @@ HomeAssistantError will be raised.
|
||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
import urlparse
|
import urllib.parse
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ def _setup_call_api(host, port, api_password):
|
||||||
data = data or {}
|
data = data or {}
|
||||||
data['api_password'] = api_password
|
data['api_password'] = api_password
|
||||||
|
|
||||||
url = urlparse.urljoin(base_url, path)
|
url = urllib.parse.urljoin(base_url, path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if method == METHOD_GET:
|
if method == METHOD_GET:
|
||||||
|
@ -61,14 +61,12 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
return json.JSONEncoder.default(self, obj)
|
return json.JSONEncoder.default(self, obj)
|
||||||
|
|
||||||
|
|
||||||
class Bus(ha.Bus):
|
class Bus(object):
|
||||||
""" Drop-in replacement for a normal bus that will forward interaction to
|
""" Drop-in replacement for a normal bus that will forward interaction to
|
||||||
a remote bus.
|
a remote bus.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host, api_password, port=None):
|
def __init__(self, host, api_password, port=None):
|
||||||
ha.Bus.__init__(self)
|
|
||||||
|
|
||||||
self.logger = logging.getLogger(__name__)
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
self._call_api = _setup_call_api(host, port, api_password)
|
self._call_api = _setup_call_api(host, port, api_password)
|
||||||
|
@ -172,6 +170,12 @@ class Bus(ha.Bus):
|
||||||
self.logger.error("Bus:{}".format(error))
|
self.logger.error("Bus:{}".format(error))
|
||||||
raise ha.HomeAssistantError(error)
|
raise ha.HomeAssistantError(error)
|
||||||
|
|
||||||
|
def has_service(self, domain, service):
|
||||||
|
""" Not implemented for remote bus.
|
||||||
|
|
||||||
|
Will throw NotImplementedError. """
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def listen_event(self, event_type, listener):
|
def listen_event(self, event_type, listener):
|
||||||
""" Not implemented for remote bus.
|
""" Not implemented for remote bus.
|
||||||
|
|
||||||
|
@ -192,14 +196,12 @@ class Bus(ha.Bus):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class StateMachine(ha.StateMachine):
|
class StateMachine(object):
|
||||||
""" Drop-in replacement for a normal statemachine that communicates with a
|
""" Drop-in replacement for a normal statemachine that communicates with a
|
||||||
remote statemachine.
|
remote statemachine.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, host, api_password, port=None):
|
def __init__(self, host, api_password, port=None):
|
||||||
ha.StateMachine.__init__(self, None)
|
|
||||||
|
|
||||||
self._call_api = _setup_call_api(host, port, api_password)
|
self._call_api = _setup_call_api(host, port, api_password)
|
||||||
|
|
||||||
self.lock = threading.Lock()
|
self.lock = threading.Lock()
|
||||||
|
@ -297,3 +299,11 @@ class StateMachine(ha.StateMachine):
|
||||||
self.logger.exception("StateMachine:Got unexpected result (2)")
|
self.logger.exception("StateMachine:Got unexpected result (2)")
|
||||||
raise ha.HomeAssistantError(
|
raise ha.HomeAssistantError(
|
||||||
"Got unexpected result (2): {}".format(req.text))
|
"Got unexpected result (2): {}".format(req.text))
|
||||||
|
|
||||||
|
def is_state(self, entity_id, state):
|
||||||
|
""" Returns True if entity exists and is specified state. """
|
||||||
|
try:
|
||||||
|
return self.get_state(entity_id).state == state
|
||||||
|
except AttributeError:
|
||||||
|
# get_state returned None
|
||||||
|
return False
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
""" Helper methods for various modules. """
|
"""
|
||||||
|
homeassistant.util
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Helper methods for various modules.
|
||||||
|
"""
|
||||||
import threading
|
import threading
|
||||||
import Queue
|
import queue
|
||||||
import datetime
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
|
||||||
RE_SANITIZE_FILENAME = re.compile(r'(~|\.\.|/|\\)')
|
RE_SANITIZE_FILENAME = re.compile(r'(~|\.\.|/|\\)')
|
||||||
RE_SLUGIFY = re.compile(r'[^A-Za-z0-9_]+')
|
RE_SLUGIFY = re.compile(r'[^A-Za-z0-9_]+')
|
||||||
|
|
||||||
DATE_STR_FORMAT = u"%H:%M:%S %d-%m-%Y"
|
DATE_STR_FORMAT = "%H:%M:%S %d-%m-%Y"
|
||||||
|
|
||||||
|
|
||||||
def sanitize_filename(filename):
|
def sanitize_filename(filename):
|
||||||
|
@ -59,13 +64,13 @@ def filter_entity_ids(entity_ids, domain_filter=None, strip_domain=False):
|
||||||
def repr_helper(inp):
|
def repr_helper(inp):
|
||||||
""" Helps creating a more readable string representation of objects. """
|
""" Helps creating a more readable string representation of objects. """
|
||||||
if isinstance(inp, dict):
|
if isinstance(inp, dict):
|
||||||
return u", ".join(
|
return ", ".join(
|
||||||
repr_helper(key)+u"="+repr_helper(item) for key, item
|
repr_helper(key)+"="+repr_helper(item) for key, item
|
||||||
in inp.items())
|
in inp.items())
|
||||||
elif isinstance(inp, datetime.datetime):
|
elif isinstance(inp, datetime.datetime):
|
||||||
return datetime_to_str(inp)
|
return datetime_to_str(inp)
|
||||||
else:
|
else:
|
||||||
return unicode(inp)
|
return str(inp)
|
||||||
|
|
||||||
|
|
||||||
# Taken from: http://www.cse.unr.edu/~quiroz/inc/colortransforms.py
|
# Taken from: http://www.cse.unr.edu/~quiroz/inc/colortransforms.py
|
||||||
|
@ -146,25 +151,38 @@ class ThreadPool(object):
|
||||||
|
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
def __init__(self, worker_count, job_handler):
|
def __init__(self, worker_count, job_handler):
|
||||||
queue = self.queue = Queue.PriorityQueue()
|
work_queue = self.work_queue = queue.PriorityQueue()
|
||||||
current_jobs = self.current_jobs = []
|
current_jobs = self.current_jobs = []
|
||||||
|
|
||||||
for _ in xrange(worker_count):
|
for _ in range(worker_count):
|
||||||
worker = threading.Thread(target=_threadpool_worker,
|
worker = threading.Thread(target=_threadpool_worker,
|
||||||
args=(queue, current_jobs, job_handler))
|
args=(work_queue, current_jobs,
|
||||||
|
job_handler))
|
||||||
worker.daemon = True
|
worker.daemon = True
|
||||||
worker.start()
|
worker.start()
|
||||||
|
|
||||||
def add_job(self, priority, job):
|
def add_job(self, priority, job):
|
||||||
""" Add a job to be sent to the workers. """
|
""" Add a job to be sent to the workers. """
|
||||||
self.queue.put((priority, job))
|
self.work_queue.put(PriorityQueueItem(priority, job))
|
||||||
|
|
||||||
|
|
||||||
def _threadpool_worker(queue, current_jobs, job_handler):
|
class PriorityQueueItem(object):
|
||||||
|
""" Holds a priority and a value. Used within PriorityQueue. """
|
||||||
|
|
||||||
|
# pylint: disable=too-few-public-methods
|
||||||
|
def __init__(self, priority, item):
|
||||||
|
self.priority = priority
|
||||||
|
self.item = item
|
||||||
|
|
||||||
|
def __lt__(self, other):
|
||||||
|
return self.priority < other.priority
|
||||||
|
|
||||||
|
|
||||||
|
def _threadpool_worker(work_queue, current_jobs, job_handler):
|
||||||
""" Provides the base functionality of a worker for the thread pool. """
|
""" Provides the base functionality of a worker for the thread pool. """
|
||||||
while True:
|
while True:
|
||||||
# Get new item from queue
|
# Get new item from work_queue
|
||||||
job = queue.get()[1]
|
job = work_queue.get().item
|
||||||
|
|
||||||
# Add to current running jobs
|
# Add to current running jobs
|
||||||
job_log = (datetime.datetime.now(), job)
|
job_log = (datetime.datetime.now(), job)
|
||||||
|
@ -176,5 +194,5 @@ def _threadpool_worker(queue, current_jobs, job_handler):
|
||||||
# Remove from current running job
|
# Remove from current running job
|
||||||
current_jobs.remove(job_log)
|
current_jobs.remove(job_log)
|
||||||
|
|
||||||
# Tell queue a task is done
|
# Tell work_queue a task is done
|
||||||
queue.task_done()
|
work_queue.task_done()
|
||||||
|
|
|
@ -1,2 +1 @@
|
||||||
python -B -m unittest homeassistant.test
|
python3 -B -m unittest homeassistant.test
|
||||||
|
|
||||||
|
|
1
start.py
1
start.py
|
@ -1,4 +1,3 @@
|
||||||
#!/usr/bin/python2
|
|
||||||
""" Starts home assistant with all possible functionality. """
|
""" Starts home assistant with all possible functionality. """
|
||||||
|
|
||||||
import homeassistant.bootstrap
|
import homeassistant.bootstrap
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue