def test_override_single_value():
    """Test values with exact match."""
    store = EV({ent: {'key': 'value'}})
    assert store.get(ent) == {'key': 'value'}
    assert len(store._cache) == 1
    assert store.get(ent) == {'key': 'value'}
    assert len(store._cache) == 1
def test_exact_overrules_domain():
    """Test exact overrules domain match."""
    store = EV(
        exact={'test.test': {'key': 'exact'}},
        domain={'test': {'key': 'domain'}},
        glob={'test.?e*': {'key': 'glob'}})
    assert store.get(ent) == {'key': 'exact'}
예제 #3
0
def test_override_single_value():
    """Test values with exact match."""
    store = EV({ent: {"key": "value"}})
    assert store.get(ent) == {"key": "value"}
    assert len(store._cache) == 1
    assert store.get(ent) == {"key": "value"}
    assert len(store._cache) == 1
예제 #4
0
def test_glob_order():
    """Test merging glob, domain and exact configs."""
    glob = OrderedDict()
    glob["test.*est"] = {"value": "first"}
    glob["test.*"] = {"value": "second"}

    store = EV(glob=glob)
    assert store.get(ent) == {"value": "second"}
예제 #5
0
def test_glob_overrules_domain():
    """Test domain overrules glob match."""
    store = EV(domain={"test": {
        "key": "domain"
    }},
               glob={"test.?e*": {
                   "key": "glob"
               }})
    assert store.get(ent) == {"key": "glob"}
예제 #6
0
def test_glob_overrules_domain():
    """Test domain overrules glob match."""
    store = EV(domain={'test': {
        'key': 'domain'
    }},
               glob={'test.?e*': {
                   'key': 'glob'
               }})
    assert store.get(ent) == {'key': 'glob'}
def test_glob_order():
    """Test merging glob, domain and exact configs."""
    glob = OrderedDict()
    glob['test.*est'] = {"value": "first"}
    glob['test.*'] = {"value": "second"}

    store = EV(glob=glob)
    assert store.get(ent) == {
        'value': 'second'
    }
def test_merging_values():
    """Test merging glob, domain and exact configs."""
    store = EV(
        exact={'test.test': {'exact_key': 'exact'}},
        domain={'test': {'domain_key': 'domain'}},
        glob={'test.?e*': {'glob_key': 'glob'}})
    assert store.get(ent) == {
        'exact_key': 'exact',
        'domain_key': 'domain',
        'glob_key': 'glob',
    }
예제 #9
0
def test_exact_overrules_domain():
    """Test exact overrules domain match."""
    store = EV(exact={'test.test': {
        'key': 'exact'
    }},
               domain={'test': {
                   'key': 'domain'
               }},
               glob={'test.?e*': {
                   'key': 'glob'
               }})
    assert store.get(ent) == {'key': 'exact'}
예제 #10
0
def test_exact_overrules_domain():
    """Test exact overrules domain match."""
    store = EV(
        exact={"test.test": {
            "key": "exact"
        }},
        domain={"test": {
            "key": "domain"
        }},
        glob={"test.?e*": {
            "key": "glob"
        }},
    )
    assert store.get(ent) == {"key": "exact"}
예제 #11
0
def setup(hass, config):
    """Activate Prometheus component."""
    hass.http.register_view(PrometheusView(prometheus_client))

    conf = config[DOMAIN]
    entity_filter = conf[CONF_FILTER]
    namespace = conf.get(CONF_PROM_NAMESPACE)
    climate_units = hass.config.units.temperature_unit
    override_metric = conf.get(CONF_OVERRIDE_METRIC)
    default_metric = conf.get(CONF_DEFAULT_METRIC)
    component_config = EntityValues(
        conf[CONF_COMPONENT_CONFIG],
        conf[CONF_COMPONENT_CONFIG_DOMAIN],
        conf[CONF_COMPONENT_CONFIG_GLOB],
    )

    metrics = PrometheusMetrics(
        prometheus_client,
        entity_filter,
        namespace,
        climate_units,
        component_config,
        override_metric,
        default_metric,
    )

    hass.bus.listen(EVENT_STATE_CHANGED, metrics.handle_event)
    return True
예제 #12
0
def test_merging_values():
    """Test merging glob, domain and exact configs."""
    store = EV(exact={'test.test': {
        'exact_key': 'exact'
    }},
               domain={'test': {
                   'domain_key': 'domain'
               }},
               glob={'test.?e*': {
                   'glob_key': 'glob'
               }})
    assert store.get(ent) == {
        'exact_key': 'exact',
        'domain_key': 'domain',
        'glob_key': 'glob',
    }
예제 #13
0
    def test_overwriting_hidden_property_to_true(self):
        """Test we can overwrite hidden property to True."""
        self.hass.data[DATA_CUSTOMIZE] = EntityValues({
            self.entity.entity_id: {ATTR_HIDDEN: True}})
        self.entity.schedule_update_ha_state()
        self.hass.block_till_done()

        state = self.hass.states.get(self.entity.entity_id)
        assert state.attributes.get(ATTR_HIDDEN)
예제 #14
0
def test_merging_values():
    """Test merging glob, domain and exact configs."""
    store = EV(
        exact={"test.test": {
            "exact_key": "exact"
        }},
        domain={"test": {
            "domain_key": "domain"
        }},
        glob={"test.?e*": {
            "glob_key": "glob"
        }},
    )
    assert store.get(ent) == {
        "exact_key": "exact",
        "domain_key": "domain",
        "glob_key": "glob",
    }
예제 #15
0
def test_override_by_glob():
    """Test values with glob match."""
    store = EV(glob={"test.?e*": {"key": "value"}})
    assert store.get(ent) == {"key": "value"}
예제 #16
0
def test_override_by_domain():
    """Test values with domain match."""
    store = EV(domain={"test": {"key": "value"}})
    assert store.get(ent) == {"key": "value"}
예제 #17
0
async def async_process_ha_core_config(
        hass: HomeAssistant,
        config: Dict,
        has_api_password: bool = False,
        has_trusted_networks: bool = False) -> None:
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)

    # Only load auth during startup.
    if not hasattr(hass, 'auth'):
        auth_conf = config.get(CONF_AUTH_PROVIDERS)

        if auth_conf is None:
            auth_conf = [{'type': 'homeassistant'}]
            if has_api_password:
                auth_conf.append({'type': 'legacy_api_password'})
            if has_trusted_networks:
                auth_conf.append({'type': 'trusted_networks'})

        mfa_conf = config.get(CONF_AUTH_MFA_MODULES,
                              [{
                                  'type': 'totp',
                                  'id': 'totp',
                                  'name': 'Authenticator app'
                              }])

        setattr(hass, 'auth', await
                auth.auth_manager_from_config(hass, auth_conf, mfa_conf))

    hac = hass.config

    def set_time_zone(time_zone_str: Optional[str]) -> None:
        """Help to set the time zone."""
        if time_zone_str is None:
            return

        time_zone = date_util.get_time_zone(time_zone_str)

        if time_zone:
            hac.time_zone = time_zone
            date_util.set_default_time_zone(time_zone)
        else:
            _LOGGER.error("Received invalid time zone %s", time_zone_str)

    for key, attr in ((CONF_LATITUDE, 'latitude'), (CONF_LONGITUDE,
                                                    'longitude'),
                      (CONF_NAME, 'location_name'), (CONF_ELEVATION,
                                                     'elevation')):
        if key in config:
            setattr(hac, attr, config[key])

    set_time_zone(config.get(CONF_TIME_ZONE))

    # Init whitelist external dir
    hac.whitelist_external_dirs = {hass.config.path('www')}
    if CONF_WHITELIST_EXTERNAL_DIRS in config:
        hac.whitelist_external_dirs.update(
            set(config[CONF_WHITELIST_EXTERNAL_DIRS]))

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = \
        EntityValues(cust_exact, cust_domain, cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        if unit == TEMP_CELSIUS:
            hac.units = METRIC_SYSTEM
        else:
            hac.units = IMPERIAL_SYSTEM
        _LOGGER.warning(
            "Found deprecated temperature unit in core "
            "configuration expected unit system. Replace '%s: %s' "
            "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit, CONF_UNIT_SYSTEM,
            hac.units.name)

    # Shortcut if no auto-detection necessary
    if None not in (hac.latitude, hac.longitude, hac.units, hac.time_zone,
                    hac.elevation):
        return

    discovered = []  # type: List[Tuple[str, Any]]

    # If we miss some of the needed values, auto detect them
    if None in (hac.latitude, hac.longitude, hac.units, hac.time_zone):
        info = await hass.async_add_executor_job(loc_util.detect_location_info)

        if info is None:
            _LOGGER.error("Could not detect location information")
            return

        if hac.latitude is None and hac.longitude is None:
            hac.latitude, hac.longitude = (info.latitude, info.longitude)
            discovered.append(('latitude', hac.latitude))
            discovered.append(('longitude', hac.longitude))

        if hac.units is None:
            hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM
            discovered.append((CONF_UNIT_SYSTEM, hac.units.name))

        if hac.location_name is None:
            hac.location_name = info.city
            discovered.append(('name', info.city))

        if hac.time_zone is None:
            set_time_zone(info.time_zone)
            discovered.append(('time_zone', info.time_zone))

    if hac.elevation is None and hac.latitude is not None and \
       hac.longitude is not None:
        elevation = await hass.async_add_executor_job(loc_util.elevation,
                                                      hac.latitude,
                                                      hac.longitude)
        hac.elevation = elevation
        discovered.append(('elevation', elevation))

    if discovered:
        _LOGGER.warning(
            "Incomplete core configuration. Auto detected %s",
            ", ".join('{}: {}'.format(key, val) for key, val in discovered))
예제 #18
0
def _generate_event_to_json(conf: dict) -> Callable[[dict], str]:
    """Build event to json converter and add to config."""
    entity_filter = convert_include_exclude_filter(conf)
    tags = conf.get(CONF_TAGS)
    tags_attributes = conf.get(CONF_TAGS_ATTRIBUTES)
    default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
    measurement_attr = conf.get(CONF_MEASUREMENT_ATTR)
    override_measurement = conf.get(CONF_OVERRIDE_MEASUREMENT)
    global_ignore_attributes = set(conf[CONF_IGNORE_ATTRIBUTES])
    component_config = EntityValues(
        conf[CONF_COMPONENT_CONFIG],
        conf[CONF_COMPONENT_CONFIG_DOMAIN],
        conf[CONF_COMPONENT_CONFIG_GLOB],
    )

    def event_to_json(event: dict) -> str:
        """Convert event into json in format Influx expects."""
        state = event.data.get(EVENT_NEW_STATE)
        if (
            state is None
            or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE)
            or not entity_filter(state.entity_id)
        ):
            return

        try:
            _include_state = _include_value = False

            _state_as_value = float(state.state)
            _include_value = True
        except ValueError:
            try:
                _state_as_value = float(state_helper.state_as_number(state))
                _include_state = _include_value = True
            except ValueError:
                _include_state = True

        include_uom = True
        include_dc = True
        entity_config = component_config.get(state.entity_id)
        measurement = entity_config.get(CONF_OVERRIDE_MEASUREMENT)
        if measurement in (None, ""):
            if override_measurement:
                measurement = override_measurement
            else:
                if measurement_attr == "entity_id":
                    measurement = state.entity_id
                elif measurement_attr == "domain__device_class":
                    device_class = state.attributes.get("device_class")
                    if device_class is None:
                        # This entity doesn't have a device_class set, use only domain
                        measurement = state.domain
                    else:
                        measurement = f"{state.domain}__{device_class}"
                        include_dc = False
                else:
                    measurement = state.attributes.get(measurement_attr)
                if measurement in (None, ""):
                    if default_measurement:
                        measurement = default_measurement
                    else:
                        measurement = state.entity_id
                else:
                    include_uom = measurement_attr != "unit_of_measurement"

        json = {
            INFLUX_CONF_MEASUREMENT: measurement,
            INFLUX_CONF_TAGS: {
                CONF_DOMAIN: state.domain,
                CONF_ENTITY_ID: state.object_id,
            },
            INFLUX_CONF_TIME: event.time_fired,
            INFLUX_CONF_FIELDS: {},
        }
        if _include_state:
            json[INFLUX_CONF_FIELDS][INFLUX_CONF_STATE] = state.state
        if _include_value:
            json[INFLUX_CONF_FIELDS][INFLUX_CONF_VALUE] = _state_as_value

        ignore_attributes = set(entity_config.get(CONF_IGNORE_ATTRIBUTES, []))
        ignore_attributes.update(global_ignore_attributes)
        for key, value in state.attributes.items():
            if key in tags_attributes:
                json[INFLUX_CONF_TAGS][key] = value
            elif (
                (key != CONF_UNIT_OF_MEASUREMENT or include_uom)
                and (key != "device_class" or include_dc)
                and key not in ignore_attributes
            ):
                # If the key is already in fields
                if key in json[INFLUX_CONF_FIELDS]:
                    key = f"{key}_"
                # Prevent column data errors in influxDB.
                # For each value we try to cast it as float
                # But if we can not do it we store the value
                # as string add "_str" postfix to the field key
                try:
                    json[INFLUX_CONF_FIELDS][key] = float(value)
                except (ValueError, TypeError):
                    new_key = f"{key}_str"
                    new_value = str(value)
                    json[INFLUX_CONF_FIELDS][new_key] = new_value

                    if RE_DIGIT_TAIL.match(new_value):
                        json[INFLUX_CONF_FIELDS][key] = float(
                            RE_DECIMAL.sub("", new_value)
                        )

                # Infinity and NaN are not valid floats in InfluxDB
                try:
                    if not math.isfinite(json[INFLUX_CONF_FIELDS][key]):
                        del json[INFLUX_CONF_FIELDS][key]
                except (KeyError, TypeError):
                    pass

        json[INFLUX_CONF_TAGS].update(tags)

        return json

    return event_to_json
예제 #19
0
def test_override_by_glob():
    """Test values with glob match."""
    store = EV(glob={'test.?e*': {'key': 'value'}})
    assert store.get(ent) == {'key': 'value'}
def test_override_by_domain():
    """Test values with domain match."""
    store = EV(domain={'test': {'key': 'value'}})
    assert store.get(ent) == {'key': 'value'}
예제 #21
0
def async_process_ha_core_config(hass, config):
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)
    hac = hass.config

    def set_time_zone(time_zone_str):
        """Help to set the time zone."""
        if time_zone_str is None:
            return

        time_zone = date_util.get_time_zone(time_zone_str)

        if time_zone:
            hac.time_zone = time_zone
            date_util.set_default_time_zone(time_zone)
        else:
            _LOGGER.error("Received invalid time zone %s", time_zone_str)

    for key, attr in ((CONF_LATITUDE, 'latitude'),
                      (CONF_LONGITUDE, 'longitude'),
                      (CONF_NAME, 'location_name'),
                      (CONF_ELEVATION, 'elevation')):
        if key in config:
            setattr(hac, attr, config[key])

    if CONF_TIME_ZONE in config:
        set_time_zone(config.get(CONF_TIME_ZONE))

    # Init whitelist external dir
    hac.whitelist_external_dirs = set((hass.config.path('www'),))
    if CONF_WHITELIST_EXTERNAL_DIRS in config:
        hac.whitelist_external_dirs.update(
            set(config[CONF_WHITELIST_EXTERNAL_DIRS]))

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = \
        EntityValues(cust_exact, cust_domain, cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        if unit == TEMP_CELSIUS:
            hac.units = METRIC_SYSTEM
        else:
            hac.units = IMPERIAL_SYSTEM
        _LOGGER.warning("Found deprecated temperature unit in core "
                        "configuration expected unit system. Replace '%s: %s' "
                        "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit,
                        CONF_UNIT_SYSTEM, hac.units.name)

    # Shortcut if no auto-detection necessary
    if None not in (hac.latitude, hac.longitude, hac.units,
                    hac.time_zone, hac.elevation):
        return

    discovered = []

    # If we miss some of the needed values, auto detect them
    if None in (hac.latitude, hac.longitude, hac.units,
                hac.time_zone):
        info = yield from hass.async_add_job(
            loc_util.detect_location_info)

        if info is None:
            _LOGGER.error("Could not detect location information")
            return

        if hac.latitude is None and hac.longitude is None:
            hac.latitude, hac.longitude = (info.latitude, info.longitude)
            discovered.append(('latitude', hac.latitude))
            discovered.append(('longitude', hac.longitude))

        if hac.units is None:
            hac.units = METRIC_SYSTEM if info.use_metric else IMPERIAL_SYSTEM
            discovered.append((CONF_UNIT_SYSTEM, hac.units.name))

        if hac.location_name is None:
            hac.location_name = info.city
            discovered.append(('name', info.city))

        if hac.time_zone is None:
            set_time_zone(info.time_zone)
            discovered.append(('time_zone', info.time_zone))

    if hac.elevation is None and hac.latitude is not None and \
       hac.longitude is not None:
        elevation = yield from hass.async_add_job(
            loc_util.elevation, hac.latitude, hac.longitude)
        hac.elevation = elevation
        discovered.append(('elevation', elevation))

    if discovered:
        _LOGGER.warning(
            "Incomplete core configuration. Auto detected %s",
            ", ".join('{}: {}'.format(key, val) for key, val in discovered))
예제 #22
0
def setup(hass, config):
    """Set up the InfluxDB component."""
    from influxdb import InfluxDBClient, exceptions

    conf = config[DOMAIN]

    kwargs = {
        "database": conf[CONF_DB_NAME],
        "verify_ssl": conf[CONF_VERIFY_SSL],
        "timeout": TIMEOUT,
    }

    if CONF_HOST in conf:
        kwargs["host"] = conf[CONF_HOST]

    if CONF_PORT in conf:
        kwargs["port"] = conf[CONF_PORT]

    if CONF_USERNAME in conf:
        kwargs["username"] = conf[CONF_USERNAME]

    if CONF_PASSWORD in conf:
        kwargs["password"] = conf[CONF_PASSWORD]

    if CONF_SSL in conf:
        kwargs["ssl"] = conf[CONF_SSL]

    include = conf.get(CONF_INCLUDE, {})
    exclude = conf.get(CONF_EXCLUDE, {})
    whitelist_e = set(include.get(CONF_ENTITIES, []))
    whitelist_d = set(include.get(CONF_DOMAINS, []))
    blacklist_e = set(exclude.get(CONF_ENTITIES, []))
    blacklist_d = set(exclude.get(CONF_DOMAINS, []))
    tags = conf.get(CONF_TAGS)
    tags_attributes = conf.get(CONF_TAGS_ATTRIBUTES)
    default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
    override_measurement = conf.get(CONF_OVERRIDE_MEASUREMENT)
    component_config = EntityValues(
        conf[CONF_COMPONENT_CONFIG],
        conf[CONF_COMPONENT_CONFIG_DOMAIN],
        conf[CONF_COMPONENT_CONFIG_GLOB],
    )
    max_tries = conf.get(CONF_RETRY_COUNT)

    try:
        influx = InfluxDBClient(**kwargs)
        influx.write_points([])
    except (exceptions.InfluxDBClientError,
            requests.exceptions.ConnectionError) as exc:
        _LOGGER.warning(
            "Database host is not accessible due to '%s', please "
            "check your entries in the configuration file (host, "
            "port, etc.) and verify that the database exists and is "
            "READ/WRITE. Retrying again in %s seconds.",
            exc,
            RETRY_INTERVAL,
        )
        event_helper.call_later(hass, RETRY_INTERVAL,
                                lambda _: setup(hass, config))
        return True

    def event_to_json(event):
        """Add an event to the outgoing Influx list."""
        state = event.data.get("new_state")
        if (state is None
                or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE)
                or state.entity_id in blacklist_e
                or state.domain in blacklist_d):
            return

        try:
            if ((whitelist_e or whitelist_d)
                    and state.entity_id not in whitelist_e
                    and state.domain not in whitelist_d):
                return

            _include_state = _include_value = False

            _state_as_value = float(state.state)
            _include_value = True
        except ValueError:
            try:
                _state_as_value = float(state_helper.state_as_number(state))
                _include_state = _include_value = True
            except ValueError:
                _include_state = True

        include_uom = True
        measurement = component_config.get(
            state.entity_id).get(CONF_OVERRIDE_MEASUREMENT)
        if measurement in (None, ""):
            if override_measurement:
                measurement = override_measurement
            else:
                measurement = state.attributes.get("unit_of_measurement")
                if measurement in (None, ""):
                    if default_measurement:
                        measurement = default_measurement
                    else:
                        measurement = state.entity_id
                else:
                    include_uom = False

        json = {
            "measurement": measurement,
            "tags": {
                "domain": state.domain,
                "entity_id": state.object_id
            },
            "time": event.time_fired,
            "fields": {},
        }
        if _include_state:
            json["fields"]["state"] = state.state
        if _include_value:
            json["fields"]["value"] = _state_as_value

        for key, value in state.attributes.items():
            if key in tags_attributes:
                json["tags"][key] = value
            elif key != "unit_of_measurement" or include_uom:
                # If the key is already in fields
                if key in json["fields"]:
                    key = key + "_"
                # Prevent column data errors in influxDB.
                # For each value we try to cast it as float
                # But if we can not do it we store the value
                # as string add "_str" postfix to the field key
                try:
                    json["fields"][key] = float(value)
                except (ValueError, TypeError):
                    new_key = f"{key}_str"
                    new_value = str(value)
                    json["fields"][new_key] = new_value

                    if RE_DIGIT_TAIL.match(new_value):
                        json["fields"][key] = float(
                            RE_DECIMAL.sub("", new_value))

                # Infinity and NaN are not valid floats in InfluxDB
                try:
                    if not math.isfinite(json["fields"][key]):
                        del json["fields"][key]
                except (KeyError, TypeError):
                    pass

        json["tags"].update(tags)

        return json

    instance = hass.data[DOMAIN] = InfluxThread(hass, influx, event_to_json,
                                                max_tries)
    instance.start()

    def shutdown(event):
        """Shut down the thread."""
        instance.queue.put(None)
        instance.join()
        influx.close()

    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, shutdown)

    return True
def test_glob_overrules_domain():
    """Test domain overrules glob match."""
    store = EV(
        domain={'test': {'key': 'domain'}},
        glob={'test.?e*': {'key': 'glob'}})
    assert store.get(ent) == {'key': 'glob'}
예제 #24
0
def setup(hass, config):
    mqtt = loader.get_component('mqtt')
    
    conf = config[DOMAIN]
    
    iot_devices = conf.get(CONF_IOT_DEVICES, [])
    gateway = conf.get(CONF_GATEWAY, [])
    
    if not iot_devices:
        _LOGGER.warning("No iot devices found.")
        return
    else:
        _LOGGER.info("Found %s iot devices.", len(iot_devices))
    
    if not gateway:
        _LOGGER.warning("No gateway found.")
        return
    else:
        _LOGGER.info("Found configuration for gateway: %s", gateway)
    
    # whitelist and blacklist for domains and entities
    whitelist_e = []
    entities_mapping = {}
    
    # init mapping for iot devices
    for iot_device in iot_devices:
        if CONF_ENTITY_ID in iot_device:
            entity_id = iot_device.get(CONF_ENTITY_ID)
            whitelist_e.append(entity_id)
            entities_mapping[entity_id] = {}
            entities_mapping[entity_id][CONF_PRODUCT_KEY] = iot_device.get(CONF_PRODUCT_KEY)
            entities_mapping[entity_id][CONF_DEVICE_NAME] = iot_device.get(CONF_DEVICE_NAME)
            entities_mapping[entity_id][CONF_DEVICE_SECRET] = iot_device.get(CONF_DEVICE_SECRET)
    
    _LOGGER.info("whitelist for entities: %s", whitelist_e)
    
    component_config = EntityValues(
        conf[CONF_COMPONENT_CONFIG],
        conf[CONF_COMPONENT_CONFIG_DOMAIN],
        conf[CONF_COMPONENT_CONFIG_GLOB])
    
    def topo_added(topic, payload, qos):
        """Topo added callback"""
        _LOGGER.info("Got birth/will message reply.")
        try:
            payload_json = json.loads(payload)
            if payload_json['code'] == 6207:
                for entity_id in entities_mapping:
                    if entities_mapping[entity_id]:
                        login_iot_device(gateway, entities_mapping[entity_id], mqtt, hass)
            elif payload_json['code'] == 200:
                _LOGGER.info("Device topo added, request id: %s, topic: %s", payload_json['id'], topic)
        except Exception as e:
            _LOGGER.warning("Process birth/will message reply failed.")
    
    def aliyun_iot_event_listener(event):
        """处理所有符合配置条件的 state changed 事件,转发到 aliyun iot"""
        state = event.data.get('new_state')
        
        if state is None or state.state in (
                STATE_UNKNOWN, '', STATE_UNAVAILABLE):
            return
        
        try:
            if (whitelist_e and state.entity_id not in whitelist_e):
                return

            _LOGGER.info("%s changed state to %s", state.entity_id, state.state)
            _state = int(state_helper.state_as_number(state))
            _state_key = "value"
        except ValueError:
            _state = state.state
            _state_key = "state"
        
        mapping_device = entities_mapping[state.entity_id]
        if not mapping_device:
            return
        
        whitelist_a = []
        include_attrs = component_config.get(state.entity_id).get(
            CONF_INCLUDE_ATTRIBUTES)
        # _LOGGER.info("properties can be posted: %s", include_attrs)
        if isinstance(include_attrs, str) and include_attrs not in (None, ''):
            whitelist_a = include_attrs.split(',')
        
        # Prepare payload for device property post
        payload_json = {
            'id': make_random_int_str(),
            'version': "1.0",
            'params': {
                _state_key: _state,
            },
            'method': "thing.event.property.post"
        }
        
        for key, value in state.attributes.items():
            if len(whitelist_a) > 0 and key not in whitelist_a:
                continue
            else:
                payload_json['params'][key] = value
        
        if _state_key != "state":
            payload_json['params']['state'] = state.state
        
        _LOGGER.info("iot device property post payload json: %s", payload_json)
        
        # Post device property to Aliyun IoT Link Develop Platform
        sign_data = sign_for_device(mapping_device)
        # _LOGGER.info("device client: %s", sign_data[0])
        # _LOGGER.info("device sign: %s", sign_data[1])
        
        mqtt.publish(hass, IOT_TOPICS['property_post'].format(mapping_device[CONF_PRODUCT_KEY], mapping_device[CONF_DEVICE_NAME]), json.dumps(payload_json))
    
    # Subscribe topo add reply topic
    mqtt.subscribe(hass, IOT_TOPICS['thing_topo_add_reply'].format(gateway[CONF_KEY], gateway[CONF_NAME]), topo_added)
    
    # login devices
    for entity_id in entities_mapping:
        if entities_mapping[entity_id]:
            login_iot_device(gateway, entities_mapping[entity_id], mqtt, hass)
    
    hass.bus.listen(EVENT_STATE_CHANGED, aliyun_iot_event_listener)

    return True
예제 #25
0
async def async_process_ha_core_config(hass: HomeAssistant, config: dict) -> None:
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)

    # Only load auth during startup.
    if not hasattr(hass, "auth"):
        auth_conf = config.get(CONF_AUTH_PROVIDERS)

        if auth_conf is None:
            auth_conf = [{"type": "homeassistant"}]

        mfa_conf = config.get(
            CONF_AUTH_MFA_MODULES,
            [{"type": "totp", "id": "totp", "name": "Authenticator app"}],
        )

        setattr(
            hass, "auth", await auth.auth_manager_from_config(hass, auth_conf, mfa_conf)
        )

    await hass.config.async_load()

    hac = hass.config

    if any(
        k in config
        for k in [
            CONF_LATITUDE,
            CONF_LONGITUDE,
            CONF_NAME,
            CONF_ELEVATION,
            CONF_TIME_ZONE,
            CONF_UNIT_SYSTEM,
            CONF_EXTERNAL_URL,
            CONF_INTERNAL_URL,
        ]
    ):
        hac.config_source = SOURCE_YAML

    for key, attr in (
        (CONF_LATITUDE, "latitude"),
        (CONF_LONGITUDE, "longitude"),
        (CONF_NAME, "location_name"),
        (CONF_ELEVATION, "elevation"),
        (CONF_INTERNAL_URL, "internal_url"),
        (CONF_EXTERNAL_URL, "external_url"),
        (CONF_MEDIA_DIRS, "media_dirs"),
        (CONF_LEGACY_TEMPLATES, "legacy_templates"),
    ):
        if key in config:
            setattr(hac, attr, config[key])

    if CONF_TIME_ZONE in config:
        hac.set_time_zone(config[CONF_TIME_ZONE])

    if CONF_MEDIA_DIRS not in config:
        if is_docker_env():
            hac.media_dirs = {"local": "/media"}
        else:
            hac.media_dirs = {"local": hass.config.path("media")}

    # Init whitelist external dir
    hac.allowlist_external_dirs = {hass.config.path("www"), *hac.media_dirs.values()}
    if CONF_ALLOWLIST_EXTERNAL_DIRS in config:
        hac.allowlist_external_dirs.update(set(config[CONF_ALLOWLIST_EXTERNAL_DIRS]))

    elif LEGACY_CONF_WHITELIST_EXTERNAL_DIRS in config:
        _LOGGER.warning(
            "Key %s has been replaced with %s. Please update your config",
            LEGACY_CONF_WHITELIST_EXTERNAL_DIRS,
            CONF_ALLOWLIST_EXTERNAL_DIRS,
        )
        hac.allowlist_external_dirs.update(
            set(config[LEGACY_CONF_WHITELIST_EXTERNAL_DIRS])
        )

    # Init whitelist external URL list – make sure to add / to every URL that doesn't
    # already have it so that we can properly test "path ownership"
    if CONF_ALLOWLIST_EXTERNAL_URLS in config:
        hac.allowlist_external_urls.update(
            url if url.endswith("/") else f"{url}/"
            for url in config[CONF_ALLOWLIST_EXTERNAL_URLS]
        )

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = EntityValues(cust_exact, cust_domain, cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        hac.units = METRIC_SYSTEM if unit == TEMP_CELSIUS else IMPERIAL_SYSTEM
        _LOGGER.warning(
            "Found deprecated temperature unit in core "
            "configuration expected unit system. Replace '%s: %s' "
            "with '%s: %s'",
            CONF_TEMPERATURE_UNIT,
            unit,
            CONF_UNIT_SYSTEM,
            hac.units.name,
        )
def test_override_by_glob():
    """Test values with glob match."""
    store = EV(glob={'test.?e*': {'key': 'value'}})
    assert store.get(ent) == {'key': 'value'}
예제 #27
0
def setup(hass, config):
    """Setup Z-Wave.

    Will automatically load components to support devices found on the network.
    """
    # pylint: disable=global-statement, import-error
    global NETWORK

    descriptions = conf_util.load_yaml_config_file(
        os.path.join(os.path.dirname(__file__), 'services.yaml'))

    try:
        import libopenzwave
    except ImportError:
        _LOGGER.error("You are missing required dependency Python Open "
                      "Z-Wave. Please follow instructions at: "
                      "https://home-assistant.io/components/zwave/")
        return False
    from pydispatch import dispatcher
    from openzwave.option import ZWaveOption
    from openzwave.network import ZWaveNetwork
    from openzwave.group import ZWaveGroup

    default_zwave_config_path = os.path.join(
        os.path.dirname(libopenzwave.__file__), 'config')

    # Load configuration
    use_debug = config[DOMAIN].get(CONF_DEBUG)
    autoheal = config[DOMAIN].get(CONF_AUTOHEAL)
    hass.data[DATA_DEVICE_CONFIG] = EntityValues(
        config[DOMAIN][CONF_DEVICE_CONFIG],
        config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN],
        config[DOMAIN][CONF_DEVICE_CONFIG_GLOB])

    # Setup options
    options = ZWaveOption(config[DOMAIN].get(CONF_USB_STICK_PATH),
                          user_path=hass.config.config_dir,
                          config_path=config[DOMAIN].get(
                              CONF_CONFIG_PATH, default_zwave_config_path))

    options.set_console_output(use_debug)
    options.lock()

    NETWORK = ZWaveNetwork(options, autostart=False)

    if use_debug:

        def log_all(signal, value=None):
            """Log all the signals."""
            print("")
            print("SIGNAL *****", signal)
            if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
                                    ZWaveNetwork.SIGNAL_VALUE_ADDED,
                                    ZWaveNetwork.SIGNAL_SCENE_EVENT,
                                    ZWaveNetwork.SIGNAL_NODE_EVENT,
                                    ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                                    ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED):
                pprint(_obj_to_dict(value))

            print("")

        dispatcher.connect(log_all, weak=False)

    def value_added(node, value):
        """Called when a value is added to a node on the network."""
        for (component, generic_device_class, specific_device_class,
             command_class, value_type, value_genre) in DISCOVERY_COMPONENTS:

            _LOGGER.debug("Component=%s Node_id=%s query start", component,
                          node.node_id)
            if node.generic not in generic_device_class and \
               None not in generic_device_class:
                _LOGGER.debug(
                    "node.generic %s not None and in "
                    "generic_device_class %s", node.generic,
                    generic_device_class)
                continue
            if node.specific not in specific_device_class and \
               None not in specific_device_class:
                _LOGGER.debug(
                    "node.specific %s is not None and in "
                    "specific_device_class %s", node.specific,
                    specific_device_class)
                continue
            if value.command_class not in command_class and \
               None not in command_class:
                _LOGGER.debug(
                    "value.command_class %s is not None "
                    "and in command_class %s", value.command_class,
                    command_class)
                continue
            if value_type != value.type and value_type is not None:
                _LOGGER.debug("value.type %s != value_type %s", value.type,
                              value_type)
                continue
            if value_genre != value.genre and value_genre is not None:
                _LOGGER.debug("value.genre %s != value_genre %s", value.genre,
                              value_genre)
                continue

            # Configure node
            _LOGGER.debug(
                "Adding Node_id=%s Generic_command_class=%s, "
                "Specific_command_class=%s, "
                "Command_class=%s, Value type=%s, "
                "Genre=%s as %s", node.node_id, node.generic, node.specific,
                value.command_class, value.type, value.genre, component)
            workaround_component = workaround.get_device_component_mapping(
                value)
            if workaround_component and workaround_component != component:
                if workaround_component == workaround.WORKAROUND_IGNORE:
                    _LOGGER.info("Ignoring device %s due to workaround.",
                                 "{}.{}".format(component, object_id(value)))
                    continue
                _LOGGER.debug("Using %s instead of %s", workaround_component,
                              component)
                component = workaround_component

            name = "{}.{}".format(component, object_id(value))
            node_config = hass.data[DATA_DEVICE_CONFIG].get(name)

            if node_config.get(CONF_IGNORED):
                _LOGGER.info("Ignoring device %s due to device settings.",
                             name)
                return

            polling_intensity = convert(
                node_config.get(CONF_POLLING_INTENSITY), int)
            if polling_intensity:
                value.enable_poll(polling_intensity)
            else:
                value.disable_poll()

            discovery.load_platform(
                hass, component, DOMAIN, {
                    const.ATTR_NODE_ID: node.node_id,
                    const.ATTR_VALUE_ID: value.value_id,
                }, config)

    def scene_activated(node, scene_id):
        """Called when a scene is activated on any node in the network."""
        hass.bus.fire(
            const.EVENT_SCENE_ACTIVATED, {
                ATTR_ENTITY_ID: _node_object_id(node),
                const.ATTR_OBJECT_ID: _node_object_id(node),
                const.ATTR_SCENE_ID: scene_id
            })

    def node_event_activated(node, value):
        """Called when a nodeevent is activated on any node in the network."""
        hass.bus.fire(
            const.EVENT_NODE_EVENT, {
                const.ATTR_OBJECT_ID: _node_object_id(node),
                const.ATTR_BASIC_LEVEL: value
            })

    def network_ready():
        """Called when all awake nodes have been queried."""
        _LOGGER.info("Zwave network is ready for use. All awake nodes"
                     " have been queried. Sleeping nodes will be"
                     " queried when they awake.")
        hass.bus.fire(const.EVENT_NETWORK_READY)

    def network_complete():
        """Called when all nodes on network have been queried."""
        _LOGGER.info("Zwave network is complete. All nodes on the network"
                     " have been queried")
        hass.bus.fire(const.EVENT_NETWORK_COMPLETE)

    dispatcher.connect(value_added,
                       ZWaveNetwork.SIGNAL_VALUE_ADDED,
                       weak=False)
    dispatcher.connect(scene_activated,
                       ZWaveNetwork.SIGNAL_SCENE_EVENT,
                       weak=False)
    dispatcher.connect(node_event_activated,
                       ZWaveNetwork.SIGNAL_NODE_EVENT,
                       weak=False)
    dispatcher.connect(network_ready,
                       ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                       weak=False)
    dispatcher.connect(network_complete,
                       ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED,
                       weak=False)

    def add_node(service):
        """Switch into inclusion mode."""
        _LOGGER.info("Zwave add_node have been initialized.")
        NETWORK.controller.add_node()

    def add_node_secure(service):
        """Switch into secure inclusion mode."""
        _LOGGER.info("Zwave add_node_secure have been initialized.")
        NETWORK.controller.add_node(True)

    def remove_node(service):
        """Switch into exclusion mode."""
        _LOGGER.info("Zwave remove_node have been initialized.")
        NETWORK.controller.remove_node()

    def cancel_command(service):
        """Cancel a running controller command."""
        _LOGGER.info("Cancel running ZWave command.")
        NETWORK.controller.cancel_command()

    def heal_network(service):
        """Heal the network."""
        _LOGGER.info("ZWave heal running.")
        NETWORK.heal()

    def soft_reset(service):
        """Soft reset the controller."""
        _LOGGER.info("Zwave soft_reset have been initialized.")
        NETWORK.controller.soft_reset()

    def test_network(service):
        """Test the network by sending commands to all the nodes."""
        _LOGGER.info("Zwave test_network have been initialized.")
        NETWORK.test()

    def stop_zwave(_service_or_event):
        """Stop Z-Wave network."""
        _LOGGER.info("Stopping ZWave network.")
        NETWORK.stop()
        if hass.state == 'RUNNING':
            hass.bus.fire(const.EVENT_NETWORK_STOP)

    def rename_node(service):
        """Rename a node."""
        state = hass.states.get(service.data.get(ATTR_ENTITY_ID))
        node_id = state.attributes.get(const.ATTR_NODE_ID)
        node = NETWORK.nodes[node_id]
        name = service.data.get(const.ATTR_NAME)
        node.name = name
        _LOGGER.info("Renamed ZWave node %d to %s", node_id, name)

    def set_config_parameter(service):
        """Set a config parameter to a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = NETWORK.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        selection = service.data.get(const.ATTR_CONFIG_VALUE)
        size = service.data.get(const.ATTR_CONFIG_SIZE, 2)
        i = 0
        for value in (node.get_values(
                class_id=const.COMMAND_CLASS_CONFIGURATION).values()):
            if value.index == param and value.type == const.TYPE_LIST:
                _LOGGER.debug('Values for parameter %s: %s', param,
                              value.data_items)
                i = len(value.data_items) - 1
        if i == 0:
            node.set_config_param(param, selection, size)
        else:
            if selection > i:
                _LOGGER.info(
                    'Config parameter selection does not exist!'
                    ' Please check zwcfg_[home_id].xml in'
                    ' your homeassistant config directory. '
                    ' Available selections are 0 to %s', i)
                return
            node.set_config_param(param, selection, size)
            _LOGGER.info(
                'Setting config parameter %s on Node %s '
                'with selection %s and size=%s', param, node_id, selection,
                size)

    def print_config_parameter(service):
        """Print a config parameter from a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = NETWORK.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        _LOGGER.info("Config parameter %s on Node %s : %s", param, node_id,
                     get_config_value(node, param))

    def print_node(service):
        """Print all information about z-wave node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = NETWORK.nodes[node_id]
        nice_print_node(node)

    def set_wakeup(service):
        """Set wake-up interval of a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = NETWORK.nodes[node_id]
        value = service.data.get(const.ATTR_CONFIG_VALUE)
        if node.can_wake_up():
            for value_id in node.get_values(
                    class_id=const.COMMAND_CLASS_WAKE_UP):
                node.values[value_id].data = value
                _LOGGER.info("Node %s wake-up set to %d", node_id, value)
        else:
            _LOGGER.info("Node %s is not wakeable", node_id)

    def change_association(service):
        """Change an association in the zwave network."""
        association_type = service.data.get(const.ATTR_ASSOCIATION)
        node_id = service.data.get(const.ATTR_NODE_ID)
        target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID)
        group = service.data.get(const.ATTR_GROUP)
        instance = service.data.get(const.ATTR_INSTANCE)

        node = ZWaveGroup(group, NETWORK, node_id)
        if association_type == 'add':
            node.add_association(target_node_id, instance)
            _LOGGER.info(
                "Adding association for node:%s in group:%s "
                "target node:%s, instance=%s", node_id, group, target_node_id,
                instance)
        if association_type == 'remove':
            node.remove_association(target_node_id, instance)
            _LOGGER.info(
                "Removing association for node:%s in group:%s "
                "target node:%s, instance=%s", node_id, group, target_node_id,
                instance)

    def start_zwave(_service_or_event):
        """Startup Z-Wave network."""
        _LOGGER.info("Starting ZWave network.")
        NETWORK.start()
        hass.bus.fire(const.EVENT_NETWORK_START)

        # Need to be in STATE_AWAKED before talking to nodes.
        # Wait up to NETWORK_READY_WAIT_SECS seconds for the zwave network
        # to be ready.
        for i in range(const.NETWORK_READY_WAIT_SECS):
            _LOGGER.debug("network state: %d %s", NETWORK.state,
                          NETWORK.state_str)
            if NETWORK.state >= NETWORK.STATE_AWAKED:
                _LOGGER.info("zwave ready after %d seconds", i)
                break
            time.sleep(1)
        else:
            _LOGGER.warning(
                "zwave not ready after %d seconds, continuing anyway",
                const.NETWORK_READY_WAIT_SECS)
            _LOGGER.info("final network state: %d %s", NETWORK.state,
                         NETWORK.state_str)

        polling_interval = convert(config[DOMAIN].get(CONF_POLLING_INTERVAL),
                                   int)
        if polling_interval is not None:
            NETWORK.set_poll_interval(polling_interval, False)

        poll_interval = NETWORK.get_poll_interval()
        _LOGGER.info("zwave polling interval set to %d ms", poll_interval)

        hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_zwave)

        # Register node services for Z-Wave network
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node,
                               descriptions[const.SERVICE_ADD_NODE])
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE,
                               add_node_secure,
                               descriptions[const.SERVICE_ADD_NODE_SECURE])
        hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node,
                               descriptions[const.SERVICE_REMOVE_NODE])
        hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND,
                               cancel_command,
                               descriptions[const.SERVICE_CANCEL_COMMAND])
        hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK,
                               heal_network,
                               descriptions[const.SERVICE_HEAL_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset,
                               descriptions[const.SERVICE_SOFT_RESET])
        hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK,
                               test_network,
                               descriptions[const.SERVICE_TEST_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK, stop_zwave,
                               descriptions[const.SERVICE_STOP_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_START_NETWORK,
                               start_zwave,
                               descriptions[const.SERVICE_START_NETWORK])
        hass.services.register(DOMAIN,
                               const.SERVICE_RENAME_NODE,
                               rename_node,
                               descriptions[const.SERVICE_RENAME_NODE],
                               schema=RENAME_NODE_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_SET_CONFIG_PARAMETER,
            set_config_parameter,
            descriptions[const.SERVICE_SET_CONFIG_PARAMETER],
            schema=SET_CONFIG_PARAMETER_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_PRINT_CONFIG_PARAMETER,
            print_config_parameter,
            descriptions[const.SERVICE_PRINT_CONFIG_PARAMETER],
            schema=PRINT_CONFIG_PARAMETER_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_CHANGE_ASSOCIATION,
                               change_association,
                               descriptions[const.SERVICE_CHANGE_ASSOCIATION],
                               schema=CHANGE_ASSOCIATION_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_SET_WAKEUP,
                               set_wakeup,
                               descriptions[const.SERVICE_SET_WAKEUP],
                               schema=SET_WAKEUP_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_PRINT_NODE,
                               print_node,
                               descriptions[const.SERVICE_PRINT_NODE],
                               schema=PRINT_NODE_SCHEMA)

    # Setup autoheal
    if autoheal:
        _LOGGER.info("ZWave network autoheal is enabled.")
        track_time_change(hass, heal_network, hour=0, minute=0, second=0)

    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

    return True
예제 #28
0
def test_override_by_domain():
    """Test values with domain match."""
    store = EV(domain={'test': {'key': 'value'}})
    assert store.get(ent) == {'key': 'value'}
예제 #29
0
async def async_process_ha_core_config(hass: HomeAssistant,
                                       config: Dict) -> None:
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)

    # Only load auth during startup.
    if not hasattr(hass, "auth"):
        auth_conf = config.get(CONF_AUTH_PROVIDERS)

        if auth_conf is None:
            auth_conf = [{"type": "homeassistant"}]

        mfa_conf = config.get(
            CONF_AUTH_MFA_MODULES,
            [{
                "type": "totp",
                "id": "totp",
                "name": "Authenticator app"
            }],
        )

        setattr(hass, "auth", await
                auth.auth_manager_from_config(hass, auth_conf, mfa_conf))

    await hass.config.async_load()

    hac = hass.config

    if any(k in config for k in [
            CONF_LATITUDE,
            CONF_LONGITUDE,
            CONF_NAME,
            CONF_ELEVATION,
            CONF_TIME_ZONE,
            CONF_UNIT_SYSTEM,
            CONF_EXTERNAL_URL,
            CONF_INTERNAL_URL,
    ]):
        hac.config_source = SOURCE_YAML

    for key, attr in (
        (CONF_LATITUDE, "latitude"),
        (CONF_LONGITUDE, "longitude"),
        (CONF_NAME, "location_name"),
        (CONF_ELEVATION, "elevation"),
        (CONF_INTERNAL_URL, "internal_url"),
        (CONF_EXTERNAL_URL, "external_url"),
    ):
        if key in config:
            setattr(hac, attr, config[key])

    if CONF_TIME_ZONE in config:
        hac.set_time_zone(config[CONF_TIME_ZONE])

    # Init whitelist external dir
    hac.whitelist_external_dirs = {hass.config.path("www")}
    if CONF_WHITELIST_EXTERNAL_DIRS in config:
        hac.whitelist_external_dirs.update(
            set(config[CONF_WHITELIST_EXTERNAL_DIRS]))

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = EntityValues(cust_exact, cust_domain,
                                             cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        hac.units = METRIC_SYSTEM if unit == TEMP_CELSIUS else IMPERIAL_SYSTEM
        _LOGGER.warning(
            "Found deprecated temperature unit in core "
            "configuration expected unit system. Replace '%s: %s' "
            "with '%s: %s'",
            CONF_TEMPERATURE_UNIT,
            unit,
            CONF_UNIT_SYSTEM,
            hac.units.name,
        )
예제 #30
0
    for name, pkg in config[CONF_PACKAGES].items():
        if (pkg_cust := pkg.get(CONF_CORE)) is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = EntityValues(cust_exact, cust_domain,
                                             cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        hac.units = METRIC_SYSTEM if unit == TEMP_CELSIUS else IMPERIAL_SYSTEM
        _LOGGER.warning(
            "Found deprecated temperature unit in core "
            "configuration expected unit system. Replace '%s: %s' "
            "with '%s: %s'",
            CONF_TEMPERATURE_UNIT,
            unit,
예제 #31
0
def setup(hass, config):
    """Set up Z-Wave.

    Will automatically load components to support devices found on the network.
    """
    descriptions = conf_util.load_yaml_config_file(
        os.path.join(os.path.dirname(__file__), 'services.yaml'))

    from pydispatch import dispatcher
    # pylint: disable=import-error
    from openzwave.option import ZWaveOption
    from openzwave.network import ZWaveNetwork
    from openzwave.group import ZWaveGroup

    # Load configuration
    use_debug = config[DOMAIN].get(CONF_DEBUG)
    autoheal = config[DOMAIN].get(CONF_AUTOHEAL)
    device_config = EntityValues(config[DOMAIN][CONF_DEVICE_CONFIG],
                                 config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN],
                                 config[DOMAIN][CONF_DEVICE_CONFIG_GLOB])
    new_entity_ids = config[DOMAIN][CONF_NEW_ENTITY_IDS]
    if not new_entity_ids:
        _LOGGER.warning(
            "ZWave entity_ids will soon be changing. To opt in to new "
            "entity_ids now, set `new_entity_ids: true` under zwave in your "
            "configuration.yaml. See the following blog post for details: "
            "https://home-assistant.io/blog/2017/06/15/zwave-entity-ids/")

    # Setup options
    options = ZWaveOption(config[DOMAIN].get(CONF_USB_STICK_PATH),
                          user_path=hass.config.config_dir,
                          config_path=config[DOMAIN].get(CONF_CONFIG_PATH))

    options.set_console_output(use_debug)

    if CONF_NETWORK_KEY in config[DOMAIN]:
        options.addOption("NetworkKey", config[DOMAIN][CONF_NETWORK_KEY])

    options.lock()

    network = hass.data[DATA_NETWORK] = ZWaveNetwork(options, autostart=False)
    hass.data[DATA_DEVICES] = {}
    hass.data[DATA_ENTITY_VALUES] = []

    if use_debug:  # pragma: no cover

        def log_all(signal, value=None):
            """Log all the signals."""
            print("")
            print("SIGNAL *****", signal)
            if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
                                    ZWaveNetwork.SIGNAL_VALUE_ADDED,
                                    ZWaveNetwork.SIGNAL_SCENE_EVENT,
                                    ZWaveNetwork.SIGNAL_NODE_EVENT,
                                    ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                                    ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED):
                pprint(_obj_to_dict(value))

            print("")

        dispatcher.connect(log_all, weak=False)

    def value_added(node, value):
        """Handle new added value to a node on the network."""
        # Check if this value should be tracked by an existing entity
        for values in hass.data[DATA_ENTITY_VALUES]:
            values.check_value(value)

        for schema in DISCOVERY_SCHEMAS:
            if not check_node_schema(node, schema):
                continue
            if not check_value_schema(
                    value, schema[const.DISC_VALUES][const.DISC_PRIMARY]):
                continue

            values = ZWaveDeviceEntityValues(hass, schema, value, config,
                                             device_config)

            # We create a new list and update the reference here so that
            # the list can be safely iterated over in the main thread
            new_values = hass.data[DATA_ENTITY_VALUES] + [values]
            hass.data[DATA_ENTITY_VALUES] = new_values

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    def node_added(node):
        """Handle a new node on the network."""
        entity = ZWaveNodeEntity(node, network, new_entity_ids)
        name = node_name(node)
        if new_entity_ids:
            generated_id = generate_entity_id(DOMAIN + '.{}', name, [])
        else:
            generated_id = entity.entity_id
        node_config = device_config.get(generated_id)
        if node_config.get(CONF_IGNORED):
            _LOGGER.info("Ignoring node entity %s due to device settings",
                         generated_id)
            return
        component.add_entities([entity])

    def network_ready():
        """Handle the query of all awake nodes."""
        _LOGGER.info("Zwave network is ready for use. All awake nodes "
                     "have been queried. Sleeping nodes will be "
                     "queried when they awake.")
        hass.bus.fire(const.EVENT_NETWORK_READY)

    def network_complete():
        """Handle the querying of all nodes on network."""
        _LOGGER.info("Z-Wave network is complete. All nodes on the network "
                     "have been queried")
        hass.bus.fire(const.EVENT_NETWORK_COMPLETE)

    dispatcher.connect(value_added,
                       ZWaveNetwork.SIGNAL_VALUE_ADDED,
                       weak=False)
    dispatcher.connect(node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False)
    dispatcher.connect(network_ready,
                       ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                       weak=False)
    dispatcher.connect(network_complete,
                       ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED,
                       weak=False)

    def add_node(service):
        """Switch into inclusion mode."""
        _LOGGER.info("Z-Wave add_node have been initialized")
        network.controller.add_node()

    def add_node_secure(service):
        """Switch into secure inclusion mode."""
        _LOGGER.info("Z-Wave add_node_secure have been initialized")
        network.controller.add_node(True)

    def remove_node(service):
        """Switch into exclusion mode."""
        _LOGGER.info("Z-Wwave remove_node have been initialized")
        network.controller.remove_node()

    def cancel_command(service):
        """Cancel a running controller command."""
        _LOGGER.info("Cancel running Z-Wave command")
        network.controller.cancel_command()

    def heal_network(service):
        """Heal the network."""
        _LOGGER.info("Z-Wave heal running")
        network.heal()

    def soft_reset(service):
        """Soft reset the controller."""
        _LOGGER.info("Z-Wave soft_reset have been initialized")
        network.controller.soft_reset()

    def test_network(service):
        """Test the network by sending commands to all the nodes."""
        _LOGGER.info("Z-Wave test_network have been initialized")
        network.test()

    def stop_network(_service_or_event):
        """Stop Z-Wave network."""
        _LOGGER.info("Stopping Z-Wave network")
        network.stop()
        if hass.state == CoreState.running:
            hass.bus.fire(const.EVENT_NETWORK_STOP)

    def rename_node(service):
        """Rename a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        name = service.data.get(const.ATTR_NAME)
        node.name = name
        _LOGGER.info("Renamed Z-Wave node %d to %s", node_id, name)

    def rename_value(service):
        """Rename a node value."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        value_id = service.data.get(const.ATTR_VALUE_ID)
        node = network.nodes[node_id]
        value = node.values[value_id]
        name = service.data.get(const.ATTR_NAME)
        value.label = name
        _LOGGER.info("Renamed Z-Wave value (Node %d Value %d) to %s", node_id,
                     value_id, name)

    def set_poll_intensity(service):
        """Set the polling intensity of a node value."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        value_id = service.data.get(const.ATTR_VALUE_ID)
        node = network.nodes[node_id]
        value = node.values[value_id]
        intensity = service.data.get(const.ATTR_POLL_INTENSITY)
        if intensity == 0:
            if value.disable_poll():
                _LOGGER.info("Polling disabled (Node %d Value %d)", node_id,
                             value_id)
                return
            _LOGGER.info("Polling disabled failed (Node %d Value %d)", node_id,
                         value_id)
        else:
            if value.enable_poll(intensity):
                _LOGGER.info("Set polling intensity (Node %d Value %d) to %s",
                             node_id, value_id, intensity)
                return
            _LOGGER.info("Set polling intensity failed (Node %d Value %d)",
                         node_id, value_id)

    def remove_failed_node(service):
        """Remove failed node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        _LOGGER.info("Trying to remove zwave node %d", node_id)
        network.controller.remove_failed_node(node_id)

    def replace_failed_node(service):
        """Replace failed node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        _LOGGER.info("Trying to replace zwave node %d", node_id)
        network.controller.replace_failed_node(node_id)

    def set_config_parameter(service):
        """Set a config parameter to a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        selection = service.data.get(const.ATTR_CONFIG_VALUE)
        size = service.data.get(const.ATTR_CONFIG_SIZE)
        for value in (node.get_values(
                class_id=const.COMMAND_CLASS_CONFIGURATION).values()):
            if value.index != param:
                continue
            if value.type in [const.TYPE_LIST, const.TYPE_BOOL]:
                value.data = selection
                _LOGGER.info(
                    "Setting config list parameter %s on Node %s "
                    "with selection %s", param, node_id, selection)
                return
            value.data = int(selection)
            _LOGGER.info(
                "Setting config parameter %s on Node %s "
                "with selection %s", param, node_id, selection)
            return
        node.set_config_param(param, selection, size)
        _LOGGER.info(
            "Setting unknown config parameter %s on Node %s "
            "with selection %s", param, node_id, selection)

    def print_config_parameter(service):
        """Print a config parameter from a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        _LOGGER.info("Config parameter %s on Node %s: %s", param, node_id,
                     get_config_value(node, param))

    def print_node(service):
        """Print all information about z-wave node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        nice_print_node(node)

    def set_wakeup(service):
        """Set wake-up interval of a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        value = service.data.get(const.ATTR_CONFIG_VALUE)
        if node.can_wake_up():
            for value_id in node.get_values(
                    class_id=const.COMMAND_CLASS_WAKE_UP):
                node.values[value_id].data = value
                _LOGGER.info("Node %s wake-up set to %d", node_id, value)
        else:
            _LOGGER.info("Node %s is not wakeable", node_id)

    def change_association(service):
        """Change an association in the zwave network."""
        association_type = service.data.get(const.ATTR_ASSOCIATION)
        node_id = service.data.get(const.ATTR_NODE_ID)
        target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID)
        group = service.data.get(const.ATTR_GROUP)
        instance = service.data.get(const.ATTR_INSTANCE)

        node = ZWaveGroup(group, network, node_id)
        if association_type == 'add':
            node.add_association(target_node_id, instance)
            _LOGGER.info(
                "Adding association for node:%s in group:%s "
                "target node:%s, instance=%s", node_id, group, target_node_id,
                instance)
        if association_type == 'remove':
            node.remove_association(target_node_id, instance)
            _LOGGER.info(
                "Removing association for node:%s in group:%s "
                "target node:%s, instance=%s", node_id, group, target_node_id,
                instance)

    @asyncio.coroutine
    def async_refresh_entity(service):
        """Refresh values that specific entity depends on."""
        entity_id = service.data.get(ATTR_ENTITY_ID)
        async_dispatcher_send(hass,
                              SIGNAL_REFRESH_ENTITY_FORMAT.format(entity_id))

    def refresh_node(service):
        """Refresh all node info."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        node.refresh_info()

    def reset_node_meters(service):
        """Reset meter counters of a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        instance = service.data.get(const.ATTR_INSTANCE)
        node = network.nodes[node_id]
        for value in (node.get_values(
                class_id=const.COMMAND_CLASS_METER).values()):
            if value.index != const.INDEX_METER_RESET:
                continue
            if value.instance != instance:
                continue
            network.manager.pressButton(value.value_id)
            network.manager.releaseButton(value.value_id)
            _LOGGER.info("Resetting meters on node %s instance %s....",
                         node_id, instance)
            return
        _LOGGER.info(
            "Node %s on instance %s does not have resettable "
            "meters.", node_id, instance)

    def start_zwave(_service_or_event):
        """Startup Z-Wave network."""
        _LOGGER.info("Starting Z-Wave network...")
        network.start()
        hass.bus.fire(const.EVENT_NETWORK_START)

        # Need to be in STATE_AWAKED before talking to nodes.
        # Wait up to NETWORK_READY_WAIT_SECS seconds for the zwave network
        # to be ready.
        for i in range(const.NETWORK_READY_WAIT_SECS):
            _LOGGER.debug("network state: %d %s", network.state,
                          network.state_str)
            if network.state >= network.STATE_AWAKED:
                _LOGGER.info("Z-Wave ready after %d seconds", i)
                break
            time.sleep(1)
        else:
            _LOGGER.warning(
                "zwave not ready after %d seconds, continuing anyway",
                const.NETWORK_READY_WAIT_SECS)
            _LOGGER.info("final network state: %d %s", network.state,
                         network.state_str)

        polling_interval = convert(config[DOMAIN].get(CONF_POLLING_INTERVAL),
                                   int)
        if polling_interval is not None:
            network.set_poll_interval(polling_interval, False)

        poll_interval = network.get_poll_interval()
        _LOGGER.info("Z-Wave polling interval set to %d ms", poll_interval)

        hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_network)

        # Register node services for Z-Wave network
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node,
                               descriptions[const.SERVICE_ADD_NODE])
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE,
                               add_node_secure,
                               descriptions[const.SERVICE_ADD_NODE_SECURE])
        hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node,
                               descriptions[const.SERVICE_REMOVE_NODE])
        hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND,
                               cancel_command,
                               descriptions[const.SERVICE_CANCEL_COMMAND])
        hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK,
                               heal_network,
                               descriptions[const.SERVICE_HEAL_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset,
                               descriptions[const.SERVICE_SOFT_RESET])
        hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK,
                               test_network,
                               descriptions[const.SERVICE_TEST_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK,
                               stop_network,
                               descriptions[const.SERVICE_STOP_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_START_NETWORK,
                               start_zwave,
                               descriptions[const.SERVICE_START_NETWORK])
        hass.services.register(DOMAIN,
                               const.SERVICE_RENAME_NODE,
                               rename_node,
                               descriptions[const.SERVICE_RENAME_NODE],
                               schema=RENAME_NODE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_RENAME_VALUE,
                               rename_value,
                               descriptions[const.SERVICE_RENAME_VALUE],
                               schema=RENAME_VALUE_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_SET_CONFIG_PARAMETER,
            set_config_parameter,
            descriptions[const.SERVICE_SET_CONFIG_PARAMETER],
            schema=SET_CONFIG_PARAMETER_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_PRINT_CONFIG_PARAMETER,
            print_config_parameter,
            descriptions[const.SERVICE_PRINT_CONFIG_PARAMETER],
            schema=PRINT_CONFIG_PARAMETER_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REMOVE_FAILED_NODE,
                               remove_failed_node,
                               descriptions[const.SERVICE_REMOVE_FAILED_NODE],
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REPLACE_FAILED_NODE,
                               replace_failed_node,
                               descriptions[const.SERVICE_REPLACE_FAILED_NODE],
                               schema=NODE_SERVICE_SCHEMA)

        hass.services.register(DOMAIN,
                               const.SERVICE_CHANGE_ASSOCIATION,
                               change_association,
                               descriptions[const.SERVICE_CHANGE_ASSOCIATION],
                               schema=CHANGE_ASSOCIATION_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_SET_WAKEUP,
                               set_wakeup,
                               descriptions[const.SERVICE_SET_WAKEUP],
                               schema=SET_WAKEUP_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_PRINT_NODE,
                               print_node,
                               descriptions[const.SERVICE_PRINT_NODE],
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REFRESH_ENTITY,
                               async_refresh_entity,
                               descriptions[const.SERVICE_REFRESH_ENTITY],
                               schema=REFRESH_ENTITY_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REFRESH_NODE,
                               refresh_node,
                               descriptions[const.SERVICE_REFRESH_NODE],
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_RESET_NODE_METERS,
                               reset_node_meters,
                               descriptions[const.SERVICE_RESET_NODE_METERS],
                               schema=RESET_NODE_METERS_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_SET_POLL_INTENSITY,
                               set_poll_intensity,
                               descriptions[const.SERVICE_SET_POLL_INTENSITY],
                               schema=SET_POLL_INTENSITY_SCHEMA)

    # Setup autoheal
    if autoheal:
        _LOGGER.info("Z-Wave network autoheal is enabled")
        track_time_change(hass, heal_network, hour=0, minute=0, second=0)

    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

    return True
예제 #32
0
def setup(hass, config):
    """Set up the InfluxDB component."""
    from influxdb import InfluxDBClient, exceptions

    conf = config[DOMAIN]

    kwargs = {
        'database': conf[CONF_DB_NAME],
        'verify_ssl': conf[CONF_VERIFY_SSL],
        'timeout': TIMEOUT
    }

    if CONF_HOST in conf:
        kwargs['host'] = conf[CONF_HOST]

    if CONF_PORT in conf:
        kwargs['port'] = conf[CONF_PORT]

    if CONF_USERNAME in conf:
        kwargs['username'] = conf[CONF_USERNAME]

    if CONF_PASSWORD in conf:
        kwargs['password'] = conf[CONF_PASSWORD]

    if CONF_SSL in conf:
        kwargs['ssl'] = conf[CONF_SSL]

    include = conf.get(CONF_INCLUDE, {})
    exclude = conf.get(CONF_EXCLUDE, {})
    whitelist_e = set(include.get(CONF_ENTITIES, []))
    whitelist_d = set(include.get(CONF_DOMAINS, []))
    blacklist_e = set(exclude.get(CONF_ENTITIES, []))
    blacklist_d = set(exclude.get(CONF_DOMAINS, []))
    tags = conf.get(CONF_TAGS)
    tags_attributes = conf.get(CONF_TAGS_ATTRIBUTES)
    default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
    override_measurement = conf.get(CONF_OVERRIDE_MEASUREMENT)
    component_config = EntityValues(
        conf[CONF_COMPONENT_CONFIG],
        conf[CONF_COMPONENT_CONFIG_DOMAIN],
        conf[CONF_COMPONENT_CONFIG_GLOB])
    max_tries = conf.get(CONF_RETRY_COUNT)
    queue_limit = conf.get(CONF_RETRY_QUEUE)

    try:
        influx = InfluxDBClient(**kwargs)
        influx.query("SHOW SERIES LIMIT 1;", database=conf[CONF_DB_NAME])
    except (exceptions.InfluxDBClientError,
            requests.exceptions.ConnectionError) as exc:
        _LOGGER.error("Database host is not accessible due to '%s', please "
                      "check your entries in the configuration file (host, "
                      "port, etc.) and verify that the database exists and is "
                      "READ/WRITE.", exc)
        return False

    def influx_event_listener(event):
        """Listen for new messages on the bus and sends them to Influx."""
        state = event.data.get('new_state')
        if state is None or state.state in (
                STATE_UNKNOWN, '', STATE_UNAVAILABLE) or \
                state.entity_id in blacklist_e or \
                state.domain in blacklist_d:
            return

        try:
            if (whitelist_e and state.entity_id not in whitelist_e) or \
                    (whitelist_d and state.domain not in whitelist_d):
                return

            _include_state = _include_value = False

            _state_as_value = float(state.state)
            _include_value = True
        except ValueError:
            try:
                _state_as_value = float(state_helper.state_as_number(state))
                _include_state = _include_value = True
            except ValueError:
                _include_state = True

        include_uom = True
        measurement = component_config.get(state.entity_id).get(
            CONF_OVERRIDE_MEASUREMENT)
        if measurement in (None, ''):
            if override_measurement:
                measurement = override_measurement
            else:
                measurement = state.attributes.get('unit_of_measurement')
                if measurement in (None, ''):
                    if default_measurement:
                        measurement = default_measurement
                    else:
                        measurement = state.entity_id
                else:
                    include_uom = False

        json_body = [
            {
                'measurement': measurement,
                'tags': {
                    'domain': state.domain,
                    'entity_id': state.object_id,
                },
                'time': event.time_fired,
                'fields': {
                }
            }
        ]
        if _include_state:
            json_body[0]['fields']['state'] = state.state
        if _include_value:
            json_body[0]['fields']['value'] = _state_as_value

        for key, value in state.attributes.items():
            if key in tags_attributes:
                json_body[0]['tags'][key] = value
            elif key != 'unit_of_measurement' or include_uom:
                # If the key is already in fields
                if key in json_body[0]['fields']:
                    key = key + "_"
                # Prevent column data errors in influxDB.
                # For each value we try to cast it as float
                # But if we can not do it we store the value
                # as string add "_str" postfix to the field key
                try:
                    json_body[0]['fields'][key] = float(value)
                except (ValueError, TypeError):
                    new_key = "{}_str".format(key)
                    new_value = str(value)
                    json_body[0]['fields'][new_key] = new_value

                    if RE_DIGIT_TAIL.match(new_value):
                        json_body[0]['fields'][key] = float(
                            RE_DECIMAL.sub('', new_value))

        json_body[0]['tags'].update(tags)

        _write_data(json_body)

    @RetryOnError(hass, retry_limit=max_tries, retry_delay=20,
                  queue_limit=queue_limit)
    def _write_data(json_body):
        """Write the data."""
        try:
            influx.write_points(json_body)
        except exceptions.InfluxDBClientError:
            _LOGGER.exception("Error saving event %s to InfluxDB", json_body)

    hass.bus.listen(EVENT_STATE_CHANGED, influx_event_listener)

    return True
예제 #33
0
async def async_setup_entry(hass, config_entry):
    """Set up Z-Wave from a config entry.

    Will automatically load components to support devices found on the network.
    """
    from pydispatch import dispatcher

    # pylint: disable=import-error
    from openzwave.option import ZWaveOption
    from openzwave.network import ZWaveNetwork
    from openzwave.group import ZWaveGroup

    # Merge config entry and yaml config
    config = config_entry.data
    if DATA_ZWAVE_CONFIG in hass.data:
        config = {**config, **hass.data[DATA_ZWAVE_CONFIG]}

    # Update hass.data with merged config so we can access it elsewhere
    hass.data[DATA_ZWAVE_CONFIG] = config

    # Load configuration
    use_debug = config.get(CONF_DEBUG, DEFAULT_DEBUG)
    autoheal = config.get(CONF_AUTOHEAL, DEFAULT_CONF_AUTOHEAL)
    device_config = EntityValues(
        config.get(CONF_DEVICE_CONFIG),
        config.get(CONF_DEVICE_CONFIG_DOMAIN),
        config.get(CONF_DEVICE_CONFIG_GLOB),
    )

    usb_path = config[CONF_USB_STICK_PATH]

    _LOGGER.info("Z-Wave USB path is %s", usb_path)

    # Setup options
    options = ZWaveOption(
        usb_path,
        user_path=hass.config.config_dir,
        config_path=config.get(CONF_CONFIG_PATH),
    )

    options.set_console_output(use_debug)

    if config.get(CONF_NETWORK_KEY):
        options.addOption("NetworkKey", config[CONF_NETWORK_KEY])

    await hass.async_add_executor_job(options.lock)
    network = hass.data[DATA_NETWORK] = ZWaveNetwork(options, autostart=False)
    hass.data[DATA_DEVICES] = {}
    hass.data[DATA_ENTITY_VALUES] = []

    registry = await async_get_registry(hass)

    wsapi.async_load_websocket_api(hass)

    if use_debug:  # pragma: no cover

        def log_all(signal, value=None):
            """Log all the signals."""
            print("")
            print("SIGNAL *****", signal)
            if value and signal in (
                    ZWaveNetwork.SIGNAL_VALUE_CHANGED,
                    ZWaveNetwork.SIGNAL_VALUE_ADDED,
                    ZWaveNetwork.SIGNAL_SCENE_EVENT,
                    ZWaveNetwork.SIGNAL_NODE_EVENT,
                    ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                    ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED,
                    ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED_SOME_DEAD,
            ):
                pprint(_obj_to_dict(value))

            print("")

        dispatcher.connect(log_all, weak=False)

    def value_added(node, value):
        """Handle new added value to a node on the network."""
        # Check if this value should be tracked by an existing entity
        for values in hass.data[DATA_ENTITY_VALUES]:
            values.check_value(value)

        for schema in DISCOVERY_SCHEMAS:
            if not check_node_schema(node, schema):
                continue
            if not check_value_schema(
                    value, schema[const.DISC_VALUES][const.DISC_PRIMARY]):
                continue

            values = ZWaveDeviceEntityValues(hass, schema, value, config,
                                             device_config, registry)

            # We create a new list and update the reference here so that
            # the list can be safely iterated over in the main thread
            new_values = hass.data[DATA_ENTITY_VALUES] + [values]
            hass.data[DATA_ENTITY_VALUES] = new_values

    platform = EntityPlatform(
        hass=hass,
        logger=_LOGGER,
        domain=DOMAIN,
        platform_name=DOMAIN,
        platform=None,
        scan_interval=DEFAULT_SCAN_INTERVAL,
        entity_namespace=None,
        async_entities_added_callback=lambda: None,
    )
    platform.config_entry = config_entry

    def node_added(node):
        """Handle a new node on the network."""
        entity = ZWaveNodeEntity(node, network)

        async def _add_node_to_component():
            if hass.data[DATA_DEVICES].get(entity.unique_id):
                return

            name = node_name(node)
            generated_id = generate_entity_id(DOMAIN + ".{}", name, [])
            node_config = device_config.get(generated_id)
            if node_config.get(CONF_IGNORED):
                _LOGGER.info("Ignoring node entity %s due to device settings",
                             generated_id)
                return

            hass.data[DATA_DEVICES][entity.unique_id] = entity
            await platform.async_add_entities([entity])

        if entity.unique_id:
            hass.async_add_job(_add_node_to_component())
            return

        @callback
        def _on_ready(sec):
            _LOGGER.info("Z-Wave node %d ready after %d seconds",
                         entity.node_id, sec)
            hass.async_add_job(_add_node_to_component)

        @callback
        def _on_timeout(sec):
            _LOGGER.warning(
                "Z-Wave node %d not ready after %d seconds, "
                "continuing anyway",
                entity.node_id,
                sec,
            )
            hass.async_add_job(_add_node_to_component)

        hass.add_job(check_has_unique_id, entity, _on_ready, _on_timeout)

    def node_removed(node):
        node_id = node.node_id
        node_key = f"node-{node_id}"
        _LOGGER.info("Node Removed: %s", hass.data[DATA_DEVICES][node_key])
        for key in list(hass.data[DATA_DEVICES]):
            if not key.startswith(f"{node_id}-"):
                continue

            entity = hass.data[DATA_DEVICES][key]
            _LOGGER.info("Removing Entity - value: %s - entity_id: %s", key,
                         entity.entity_id)
            hass.add_job(entity.node_removed())
            del hass.data[DATA_DEVICES][key]

        entity = hass.data[DATA_DEVICES][node_key]
        hass.add_job(entity.node_removed())
        del hass.data[DATA_DEVICES][node_key]

    def network_ready():
        """Handle the query of all awake nodes."""
        _LOGGER.info("Z-Wave network is ready for use. All awake nodes "
                     "have been queried. Sleeping nodes will be "
                     "queried when they awake.")
        hass.bus.fire(const.EVENT_NETWORK_READY)

    def network_complete():
        """Handle the querying of all nodes on network."""
        _LOGGER.info("Z-Wave network is complete. All nodes on the network "
                     "have been queried")
        hass.bus.fire(const.EVENT_NETWORK_COMPLETE)

    def network_complete_some_dead():
        """Handle the querying of all nodes on network."""
        _LOGGER.info("Z-Wave network is complete. All nodes on the network "
                     "have been queried, but some nodes are marked dead")
        hass.bus.fire(const.EVENT_NETWORK_COMPLETE_SOME_DEAD)

    dispatcher.connect(value_added,
                       ZWaveNetwork.SIGNAL_VALUE_ADDED,
                       weak=False)
    dispatcher.connect(node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False)
    dispatcher.connect(node_removed,
                       ZWaveNetwork.SIGNAL_NODE_REMOVED,
                       weak=False)
    dispatcher.connect(network_ready,
                       ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                       weak=False)
    dispatcher.connect(network_complete,
                       ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED,
                       weak=False)
    dispatcher.connect(
        network_complete_some_dead,
        ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED_SOME_DEAD,
        weak=False,
    )

    def add_node(service):
        """Switch into inclusion mode."""
        _LOGGER.info("Z-Wave add_node have been initialized")
        network.controller.add_node()

    def add_node_secure(service):
        """Switch into secure inclusion mode."""
        _LOGGER.info("Z-Wave add_node_secure have been initialized")
        network.controller.add_node(True)

    def remove_node(service):
        """Switch into exclusion mode."""
        _LOGGER.info("Z-Wave remove_node have been initialized")
        network.controller.remove_node()

    def cancel_command(service):
        """Cancel a running controller command."""
        _LOGGER.info("Cancel running Z-Wave command")
        network.controller.cancel_command()

    def heal_network(service):
        """Heal the network."""
        _LOGGER.info("Z-Wave heal running")
        network.heal()

    def soft_reset(service):
        """Soft reset the controller."""
        _LOGGER.info("Z-Wave soft_reset have been initialized")
        network.controller.soft_reset()

    def test_network(service):
        """Test the network by sending commands to all the nodes."""
        _LOGGER.info("Z-Wave test_network have been initialized")
        network.test()

    def stop_network(_service_or_event):
        """Stop Z-Wave network."""
        _LOGGER.info("Stopping Z-Wave network")
        network.stop()
        if hass.state == CoreState.running:
            hass.bus.fire(const.EVENT_NETWORK_STOP)

    async def rename_node(service):
        """Rename a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        name = service.data.get(const.ATTR_NAME)
        node.name = name
        _LOGGER.info("Renamed Z-Wave node %d to %s", node_id, name)
        update_ids = service.data.get(const.ATTR_UPDATE_IDS)
        # We want to rename the device, the node entity,
        # and all the contained entities
        node_key = f"node-{node_id}"
        entity = hass.data[DATA_DEVICES][node_key]
        await entity.node_renamed(update_ids)
        for key in list(hass.data[DATA_DEVICES]):
            if not key.startswith(f"{node_id}-"):
                continue
            entity = hass.data[DATA_DEVICES][key]
            await entity.value_renamed(update_ids)

    async def rename_value(service):
        """Rename a node value."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        value_id = service.data.get(const.ATTR_VALUE_ID)
        node = network.nodes[node_id]
        value = node.values[value_id]
        name = service.data.get(const.ATTR_NAME)
        value.label = name
        _LOGGER.info("Renamed Z-Wave value (Node %d Value %d) to %s", node_id,
                     value_id, name)
        update_ids = service.data.get(const.ATTR_UPDATE_IDS)
        value_key = f"{node_id}-{value_id}"
        entity = hass.data[DATA_DEVICES][value_key]
        await entity.value_renamed(update_ids)

    def set_poll_intensity(service):
        """Set the polling intensity of a node value."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        value_id = service.data.get(const.ATTR_VALUE_ID)
        node = network.nodes[node_id]
        value = node.values[value_id]
        intensity = service.data.get(const.ATTR_POLL_INTENSITY)
        if intensity == 0:
            if value.disable_poll():
                _LOGGER.info("Polling disabled (Node %d Value %d)", node_id,
                             value_id)
                return
            _LOGGER.info("Polling disabled failed (Node %d Value %d)", node_id,
                         value_id)
        else:
            if value.enable_poll(intensity):
                _LOGGER.info(
                    "Set polling intensity (Node %d Value %d) to %s",
                    node_id,
                    value_id,
                    intensity,
                )
                return
            _LOGGER.info("Set polling intensity failed (Node %d Value %d)",
                         node_id, value_id)

    def remove_failed_node(service):
        """Remove failed node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        _LOGGER.info("Trying to remove zwave node %d", node_id)
        network.controller.remove_failed_node(node_id)

    def replace_failed_node(service):
        """Replace failed node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        _LOGGER.info("Trying to replace zwave node %d", node_id)
        network.controller.replace_failed_node(node_id)

    def set_config_parameter(service):
        """Set a config parameter to a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        selection = service.data.get(const.ATTR_CONFIG_VALUE)
        size = service.data.get(const.ATTR_CONFIG_SIZE)
        for value in node.get_values(
                class_id=const.COMMAND_CLASS_CONFIGURATION).values():
            if value.index != param:
                continue
            if value.type == const.TYPE_BOOL:
                value.data = int(selection == "True")
                _LOGGER.info(
                    "Setting config parameter %s on Node %s "
                    "with bool selection %s",
                    param,
                    node_id,
                    str(selection),
                )
                return
            if value.type == const.TYPE_LIST:
                value.data = str(selection)
                _LOGGER.info(
                    "Setting config parameter %s on Node %s "
                    "with list selection %s",
                    param,
                    node_id,
                    str(selection),
                )
                return
            if value.type == const.TYPE_BUTTON:
                network.manager.pressButton(value.value_id)
                network.manager.releaseButton(value.value_id)
                _LOGGER.info(
                    "Setting config parameter %s on Node %s "
                    "with button selection %s",
                    param,
                    node_id,
                    selection,
                )
                return
            value.data = int(selection)
            _LOGGER.info(
                "Setting config parameter %s on Node %s "
                "with selection %s",
                param,
                node_id,
                selection,
            )
            return
        node.set_config_param(param, selection, size)
        _LOGGER.info(
            "Setting unknown config parameter %s on Node %s "
            "with selection %s",
            param,
            node_id,
            selection,
        )

    def refresh_node_value(service):
        """Refresh the specified value from a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        value_id = service.data.get(const.ATTR_VALUE_ID)
        node = network.nodes[node_id]
        node.values[value_id].refresh()
        _LOGGER.info("Node %s value %s refreshed", node_id, value_id)

    def set_node_value(service):
        """Set the specified value on a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        value_id = service.data.get(const.ATTR_VALUE_ID)
        value = service.data.get(const.ATTR_CONFIG_VALUE)
        node = network.nodes[node_id]
        node.values[value_id].data = value
        _LOGGER.info("Node %s value %s set to %s", node_id, value_id, value)

    def print_config_parameter(service):
        """Print a config parameter from a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        _LOGGER.info(
            "Config parameter %s on Node %s: %s",
            param,
            node_id,
            get_config_value(node, param),
        )

    def print_node(service):
        """Print all information about z-wave node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        nice_print_node(node)

    def set_wakeup(service):
        """Set wake-up interval of a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        value = service.data.get(const.ATTR_CONFIG_VALUE)
        if node.can_wake_up():
            for value_id in node.get_values(
                    class_id=const.COMMAND_CLASS_WAKE_UP):
                node.values[value_id].data = value
                _LOGGER.info("Node %s wake-up set to %d", node_id, value)
        else:
            _LOGGER.info("Node %s is not wakeable", node_id)

    def change_association(service):
        """Change an association in the zwave network."""
        association_type = service.data.get(const.ATTR_ASSOCIATION)
        node_id = service.data.get(const.ATTR_NODE_ID)
        target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID)
        group = service.data.get(const.ATTR_GROUP)
        instance = service.data.get(const.ATTR_INSTANCE)

        node = ZWaveGroup(group, network, node_id)
        if association_type == "add":
            node.add_association(target_node_id, instance)
            _LOGGER.info(
                "Adding association for node:%s in group:%s "
                "target node:%s, instance=%s",
                node_id,
                group,
                target_node_id,
                instance,
            )
        if association_type == "remove":
            node.remove_association(target_node_id, instance)
            _LOGGER.info(
                "Removing association for node:%s in group:%s "
                "target node:%s, instance=%s",
                node_id,
                group,
                target_node_id,
                instance,
            )

    async def async_refresh_entity(service):
        """Refresh values that specific entity depends on."""
        entity_id = service.data.get(ATTR_ENTITY_ID)
        async_dispatcher_send(hass,
                              SIGNAL_REFRESH_ENTITY_FORMAT.format(entity_id))

    def refresh_node(service):
        """Refresh all node info."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        node.refresh_info()

    def reset_node_meters(service):
        """Reset meter counters of a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        instance = service.data.get(const.ATTR_INSTANCE)
        node = network.nodes[node_id]
        for value in node.get_values(
                class_id=const.COMMAND_CLASS_METER).values():
            if value.index != const.INDEX_METER_RESET:
                continue
            if value.instance != instance:
                continue
            network.manager.pressButton(value.value_id)
            network.manager.releaseButton(value.value_id)
            _LOGGER.info("Resetting meters on node %s instance %s....",
                         node_id, instance)
            return
        _LOGGER.info(
            "Node %s on instance %s does not have resettable "
            "meters.",
            node_id,
            instance,
        )

    def heal_node(service):
        """Heal a node on the network."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        update_return_routes = service.data.get(const.ATTR_RETURN_ROUTES)
        node = network.nodes[node_id]
        _LOGGER.info("Z-Wave node heal running for node %s", node_id)
        node.heal(update_return_routes)

    def test_node(service):
        """Send test messages to a node on the network."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        messages = service.data.get(const.ATTR_MESSAGES)
        node = network.nodes[node_id]
        _LOGGER.info("Sending %s test-messages to node %s.", messages, node_id)
        node.test(messages)

    def start_zwave(_service_or_event):
        """Startup Z-Wave network."""
        _LOGGER.info("Starting Z-Wave network...")
        network.start()
        hass.bus.fire(const.EVENT_NETWORK_START)

        async def _check_awaked():
            """Wait for Z-wave awaked state (or timeout) and finalize start."""
            _LOGGER.debug("network state: %d %s", network.state,
                          network.state_str)

            start_time = dt_util.utcnow()
            while True:
                waited = int((dt_util.utcnow() - start_time).total_seconds())

                if network.state >= network.STATE_AWAKED:
                    # Need to be in STATE_AWAKED before talking to nodes.
                    _LOGGER.info("Z-Wave ready after %d seconds", waited)
                    break
                elif waited >= const.NETWORK_READY_WAIT_SECS:
                    # Wait up to NETWORK_READY_WAIT_SECS seconds for the Z-Wave
                    # network to be ready.
                    _LOGGER.warning(
                        "Z-Wave not ready after %d seconds, continuing anyway",
                        waited)
                    _LOGGER.info("final network state: %d %s", network.state,
                                 network.state_str)
                    break
                else:
                    await asyncio.sleep(1)

            hass.async_add_job(_finalize_start)

        hass.add_job(_check_awaked)

    def _finalize_start():
        """Perform final initializations after Z-Wave network is awaked."""
        polling_interval = convert(config.get(CONF_POLLING_INTERVAL), int)
        if polling_interval is not None:
            network.set_poll_interval(polling_interval, False)

        poll_interval = network.get_poll_interval()
        _LOGGER.info("Z-Wave polling interval set to %d ms", poll_interval)

        hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_network)

        # Register node services for Z-Wave network
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node)
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE,
                               add_node_secure)
        hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node)
        hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND,
                               cancel_command)
        hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK,
                               heal_network)
        hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset)
        hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK,
                               test_network)
        hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK,
                               stop_network)
        hass.services.register(DOMAIN,
                               const.SERVICE_RENAME_NODE,
                               rename_node,
                               schema=RENAME_NODE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_RENAME_VALUE,
                               rename_value,
                               schema=RENAME_VALUE_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_SET_CONFIG_PARAMETER,
            set_config_parameter,
            schema=SET_CONFIG_PARAMETER_SCHEMA,
        )
        hass.services.register(
            DOMAIN,
            const.SERVICE_SET_NODE_VALUE,
            set_node_value,
            schema=SET_NODE_VALUE_SCHEMA,
        )
        hass.services.register(
            DOMAIN,
            const.SERVICE_REFRESH_NODE_VALUE,
            refresh_node_value,
            schema=REFRESH_NODE_VALUE_SCHEMA,
        )
        hass.services.register(
            DOMAIN,
            const.SERVICE_PRINT_CONFIG_PARAMETER,
            print_config_parameter,
            schema=PRINT_CONFIG_PARAMETER_SCHEMA,
        )
        hass.services.register(
            DOMAIN,
            const.SERVICE_REMOVE_FAILED_NODE,
            remove_failed_node,
            schema=NODE_SERVICE_SCHEMA,
        )
        hass.services.register(
            DOMAIN,
            const.SERVICE_REPLACE_FAILED_NODE,
            replace_failed_node,
            schema=NODE_SERVICE_SCHEMA,
        )

        hass.services.register(
            DOMAIN,
            const.SERVICE_CHANGE_ASSOCIATION,
            change_association,
            schema=CHANGE_ASSOCIATION_SCHEMA,
        )
        hass.services.register(DOMAIN,
                               const.SERVICE_SET_WAKEUP,
                               set_wakeup,
                               schema=SET_WAKEUP_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_PRINT_NODE,
                               print_node,
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_REFRESH_ENTITY,
            async_refresh_entity,
            schema=REFRESH_ENTITY_SCHEMA,
        )
        hass.services.register(DOMAIN,
                               const.SERVICE_REFRESH_NODE,
                               refresh_node,
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_RESET_NODE_METERS,
            reset_node_meters,
            schema=RESET_NODE_METERS_SCHEMA,
        )
        hass.services.register(
            DOMAIN,
            const.SERVICE_SET_POLL_INTENSITY,
            set_poll_intensity,
            schema=SET_POLL_INTENSITY_SCHEMA,
        )
        hass.services.register(DOMAIN,
                               const.SERVICE_HEAL_NODE,
                               heal_node,
                               schema=HEAL_NODE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_TEST_NODE,
                               test_node,
                               schema=TEST_NODE_SCHEMA)

    # Setup autoheal
    if autoheal:
        _LOGGER.info("Z-Wave network autoheal is enabled")
        async_track_time_change(hass, heal_network, hour=0, minute=0, second=0)

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

    hass.services.async_register(DOMAIN, const.SERVICE_START_NETWORK,
                                 start_zwave)

    for entry_component in SUPPORTED_PLATFORMS:
        hass.async_create_task(
            hass.config_entries.async_forward_entry_setup(
                config_entry, entry_component))

    return True
예제 #34
0
def setup(hass, config):
    """Setup Z-Wave.

    Will automatically load components to support devices found on the network.
    """
    descriptions = conf_util.load_yaml_config_file(
        os.path.join(os.path.dirname(__file__), 'services.yaml'))

    try:
        import libopenzwave
    except ImportError:
        _LOGGER.error("You are missing required dependency Python Open "
                      "Z-Wave. Please follow instructions at: "
                      "https://home-assistant.io/components/zwave/")
        return False
    from pydispatch import dispatcher
    # pylint: disable=import-error
    from openzwave.option import ZWaveOption
    from openzwave.network import ZWaveNetwork
    from openzwave.group import ZWaveGroup

    default_zwave_config_path = os.path.join(
        os.path.dirname(libopenzwave.__file__), 'config')

    # Load configuration
    use_debug = config[DOMAIN].get(CONF_DEBUG)
    autoheal = config[DOMAIN].get(CONF_AUTOHEAL)
    device_config = EntityValues(config[DOMAIN][CONF_DEVICE_CONFIG],
                                 config[DOMAIN][CONF_DEVICE_CONFIG_DOMAIN],
                                 config[DOMAIN][CONF_DEVICE_CONFIG_GLOB])

    # Setup options
    options = ZWaveOption(config[DOMAIN].get(CONF_USB_STICK_PATH),
                          user_path=hass.config.config_dir,
                          config_path=config[DOMAIN].get(
                              CONF_CONFIG_PATH, default_zwave_config_path))

    options.set_console_output(use_debug)
    options.lock()

    network = hass.data[ZWAVE_NETWORK] = ZWaveNetwork(options, autostart=False)
    hass.data[DATA_ZWAVE_DICT] = {}

    if use_debug:  # pragma: no cover

        def log_all(signal, value=None):
            """Log all the signals."""
            print("")
            print("SIGNAL *****", signal)
            if value and signal in (ZWaveNetwork.SIGNAL_VALUE_CHANGED,
                                    ZWaveNetwork.SIGNAL_VALUE_ADDED,
                                    ZWaveNetwork.SIGNAL_SCENE_EVENT,
                                    ZWaveNetwork.SIGNAL_NODE_EVENT,
                                    ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                                    ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED):
                pprint(_obj_to_dict(value))

            print("")

        dispatcher.connect(log_all, weak=False)

    discovered_values = []

    def value_added(node, value):
        """Called when a value is added to a node on the network."""
        # Check if this value should be tracked by an existing entity
        for values in discovered_values:
            values.check_value(value)

        for schema in DISCOVERY_SCHEMAS:
            if not check_node_schema(node, schema):
                continue
            if not check_value_schema(
                    value, schema[const.DISC_VALUES][const.DISC_PRIMARY]):
                continue

            values = ZWaveDeviceEntityValues(hass, schema, value, config,
                                             device_config)
            discovered_values.append(values)

    component = EntityComponent(_LOGGER, DOMAIN, hass)

    def node_added(node):
        """Called when a node is added on the network."""
        entity = ZWaveNodeEntity(node, network)
        node_config = device_config.get(entity.entity_id)
        if node_config.get(CONF_IGNORED):
            _LOGGER.info("Ignoring node entity %s due to device settings.",
                         entity.entity_id)
            return
        component.add_entities([entity])

    def scene_activated(node, scene_id):
        """Called when a scene is activated on any node in the network."""
        hass.bus.fire(
            const.EVENT_SCENE_ACTIVATED, {
                ATTR_ENTITY_ID: _node_object_id(node),
                const.ATTR_OBJECT_ID: _node_object_id(node),
                const.ATTR_SCENE_ID: scene_id
            })

    def node_event_activated(node, value):
        """Called when a nodeevent is activated on any node in the network."""
        hass.bus.fire(
            const.EVENT_NODE_EVENT, {
                const.ATTR_OBJECT_ID: _node_object_id(node),
                const.ATTR_BASIC_LEVEL: value
            })

    def network_ready():
        """Called when all awake nodes have been queried."""
        _LOGGER.info("Zwave network is ready for use. All awake nodes"
                     " have been queried. Sleeping nodes will be"
                     " queried when they awake.")
        hass.bus.fire(const.EVENT_NETWORK_READY)

    def network_complete():
        """Called when all nodes on network have been queried."""
        _LOGGER.info("Zwave network is complete. All nodes on the network"
                     " have been queried")
        hass.bus.fire(const.EVENT_NETWORK_COMPLETE)

    dispatcher.connect(value_added,
                       ZWaveNetwork.SIGNAL_VALUE_ADDED,
                       weak=False)
    dispatcher.connect(node_added, ZWaveNetwork.SIGNAL_NODE_ADDED, weak=False)
    dispatcher.connect(scene_activated,
                       ZWaveNetwork.SIGNAL_SCENE_EVENT,
                       weak=False)
    dispatcher.connect(node_event_activated,
                       ZWaveNetwork.SIGNAL_NODE_EVENT,
                       weak=False)
    dispatcher.connect(network_ready,
                       ZWaveNetwork.SIGNAL_AWAKE_NODES_QUERIED,
                       weak=False)
    dispatcher.connect(network_complete,
                       ZWaveNetwork.SIGNAL_ALL_NODES_QUERIED,
                       weak=False)

    def add_node(service):
        """Switch into inclusion mode."""
        _LOGGER.info("Zwave add_node have been initialized.")
        network.controller.add_node()

    def add_node_secure(service):
        """Switch into secure inclusion mode."""
        _LOGGER.info("Zwave add_node_secure have been initialized.")
        network.controller.add_node(True)

    def remove_node(service):
        """Switch into exclusion mode."""
        _LOGGER.info("Zwave remove_node have been initialized.")
        network.controller.remove_node()

    def cancel_command(service):
        """Cancel a running controller command."""
        _LOGGER.info("Cancel running ZWave command.")
        network.controller.cancel_command()

    def heal_network(service):
        """Heal the network."""
        _LOGGER.info("ZWave heal running.")
        network.heal()

    def soft_reset(service):
        """Soft reset the controller."""
        _LOGGER.info("Zwave soft_reset have been initialized.")
        network.controller.soft_reset()

    def test_network(service):
        """Test the network by sending commands to all the nodes."""
        _LOGGER.info("Zwave test_network have been initialized.")
        network.test()

    def stop_network(_service_or_event):
        """Stop Z-Wave network."""
        _LOGGER.info("Stopping ZWave network.")
        network.stop()
        if hass.state == CoreState.running:
            hass.bus.fire(const.EVENT_NETWORK_STOP)

    def rename_node(service):
        """Rename a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = hass.data[ZWAVE_NETWORK].nodes[node_id]
        name = service.data.get(const.ATTR_NAME)
        node.name = name
        _LOGGER.info("Renamed ZWave node %d to %s", node_id, name)

    def remove_failed_node(service):
        """Remove failed node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        _LOGGER.info('Trying to remove zwave node %d', node_id)
        network.controller.remove_failed_node(node_id)

    def replace_failed_node(service):
        """Replace failed node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        _LOGGER.info('Trying to replace zwave node %d', node_id)
        network.controller.replace_failed_node(node_id)

    def set_config_parameter(service):
        """Set a config parameter to a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        selection = service.data.get(const.ATTR_CONFIG_VALUE)
        size = service.data.get(const.ATTR_CONFIG_SIZE, 2)
        i = 0
        for value in (node.get_values(
                class_id=const.COMMAND_CLASS_CONFIGURATION).values()):
            if value.index == param and value.type == const.TYPE_LIST:
                _LOGGER.debug('Values for parameter %s: %s', param,
                              value.data_items)
                i = len(value.data_items) - 1
        if i == 0:
            node.set_config_param(param, selection, size)
        else:
            if selection > i:
                _LOGGER.info(
                    'Config parameter selection does not exist!'
                    ' Please check zwcfg_[home_id].xml in'
                    ' your homeassistant config directory. '
                    ' Available selections are 0 to %s', i)
                return
            node.set_config_param(param, selection, size)
            _LOGGER.info(
                'Setting config parameter %s on Node %s '
                'with selection %s and size=%s', param, node_id, selection,
                size)

    def print_config_parameter(service):
        """Print a config parameter from a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        param = service.data.get(const.ATTR_CONFIG_PARAMETER)
        _LOGGER.info("Config parameter %s on Node %s : %s", param, node_id,
                     get_config_value(node, param))

    def print_node(service):
        """Print all information about z-wave node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        nice_print_node(node)

    def set_wakeup(service):
        """Set wake-up interval of a node."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        value = service.data.get(const.ATTR_CONFIG_VALUE)
        if node.can_wake_up():
            for value_id in node.get_values(
                    class_id=const.COMMAND_CLASS_WAKE_UP):
                node.values[value_id].data = value
                _LOGGER.info("Node %s wake-up set to %d", node_id, value)
        else:
            _LOGGER.info("Node %s is not wakeable", node_id)

    def change_association(service):
        """Change an association in the zwave network."""
        association_type = service.data.get(const.ATTR_ASSOCIATION)
        node_id = service.data.get(const.ATTR_NODE_ID)
        target_node_id = service.data.get(const.ATTR_TARGET_NODE_ID)
        group = service.data.get(const.ATTR_GROUP)
        instance = service.data.get(const.ATTR_INSTANCE)

        node = ZWaveGroup(group, network, node_id)
        if association_type == 'add':
            node.add_association(target_node_id, instance)
            _LOGGER.info(
                "Adding association for node:%s in group:%s "
                "target node:%s, instance=%s", node_id, group, target_node_id,
                instance)
        if association_type == 'remove':
            node.remove_association(target_node_id, instance)
            _LOGGER.info(
                "Removing association for node:%s in group:%s "
                "target node:%s, instance=%s", node_id, group, target_node_id,
                instance)

    @asyncio.coroutine
    def async_refresh_entity(service):
        """Refresh values that specific entity depends on."""
        entity_id = service.data.get(ATTR_ENTITY_ID)
        async_dispatcher_send(hass,
                              SIGNAL_REFRESH_ENTITY_FORMAT.format(entity_id))

    def refresh_node(service):
        """Refresh all node info."""
        node_id = service.data.get(const.ATTR_NODE_ID)
        node = network.nodes[node_id]
        node.refresh_info()

    def start_zwave(_service_or_event):
        """Startup Z-Wave network."""
        _LOGGER.info("Starting ZWave network.")
        network.start()
        hass.bus.fire(const.EVENT_NETWORK_START)

        # Need to be in STATE_AWAKED before talking to nodes.
        # Wait up to NETWORK_READY_WAIT_SECS seconds for the zwave network
        # to be ready.
        for i in range(const.NETWORK_READY_WAIT_SECS):
            _LOGGER.debug("network state: %d %s",
                          hass.data[ZWAVE_NETWORK].state, network.state_str)
            if network.state >= network.STATE_AWAKED:
                _LOGGER.info("zwave ready after %d seconds", i)
                break
            time.sleep(1)
        else:
            _LOGGER.warning(
                "zwave not ready after %d seconds, continuing anyway",
                const.NETWORK_READY_WAIT_SECS)
            _LOGGER.info("final network state: %d %s", network.state,
                         network.state_str)

        polling_interval = convert(config[DOMAIN].get(CONF_POLLING_INTERVAL),
                                   int)
        if polling_interval is not None:
            network.set_poll_interval(polling_interval, False)

        poll_interval = network.get_poll_interval()
        _LOGGER.info("zwave polling interval set to %d ms", poll_interval)

        hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_network)

        # Register node services for Z-Wave network
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE, add_node,
                               descriptions[const.SERVICE_ADD_NODE])
        hass.services.register(DOMAIN, const.SERVICE_ADD_NODE_SECURE,
                               add_node_secure,
                               descriptions[const.SERVICE_ADD_NODE_SECURE])
        hass.services.register(DOMAIN, const.SERVICE_REMOVE_NODE, remove_node,
                               descriptions[const.SERVICE_REMOVE_NODE])
        hass.services.register(DOMAIN, const.SERVICE_CANCEL_COMMAND,
                               cancel_command,
                               descriptions[const.SERVICE_CANCEL_COMMAND])
        hass.services.register(DOMAIN, const.SERVICE_HEAL_NETWORK,
                               heal_network,
                               descriptions[const.SERVICE_HEAL_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_SOFT_RESET, soft_reset,
                               descriptions[const.SERVICE_SOFT_RESET])
        hass.services.register(DOMAIN, const.SERVICE_TEST_NETWORK,
                               test_network,
                               descriptions[const.SERVICE_TEST_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_STOP_NETWORK,
                               stop_network,
                               descriptions[const.SERVICE_STOP_NETWORK])
        hass.services.register(DOMAIN, const.SERVICE_START_NETWORK,
                               start_zwave,
                               descriptions[const.SERVICE_START_NETWORK])
        hass.services.register(DOMAIN,
                               const.SERVICE_RENAME_NODE,
                               rename_node,
                               descriptions[const.SERVICE_RENAME_NODE],
                               schema=RENAME_NODE_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_SET_CONFIG_PARAMETER,
            set_config_parameter,
            descriptions[const.SERVICE_SET_CONFIG_PARAMETER],
            schema=SET_CONFIG_PARAMETER_SCHEMA)
        hass.services.register(
            DOMAIN,
            const.SERVICE_PRINT_CONFIG_PARAMETER,
            print_config_parameter,
            descriptions[const.SERVICE_PRINT_CONFIG_PARAMETER],
            schema=PRINT_CONFIG_PARAMETER_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REMOVE_FAILED_NODE,
                               remove_failed_node,
                               descriptions[const.SERVICE_REMOVE_FAILED_NODE],
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REPLACE_FAILED_NODE,
                               replace_failed_node,
                               descriptions[const.SERVICE_REPLACE_FAILED_NODE],
                               schema=NODE_SERVICE_SCHEMA)

        hass.services.register(DOMAIN,
                               const.SERVICE_CHANGE_ASSOCIATION,
                               change_association,
                               descriptions[const.SERVICE_CHANGE_ASSOCIATION],
                               schema=CHANGE_ASSOCIATION_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_SET_WAKEUP,
                               set_wakeup,
                               descriptions[const.SERVICE_SET_WAKEUP],
                               schema=SET_WAKEUP_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_PRINT_NODE,
                               print_node,
                               descriptions[const.SERVICE_PRINT_NODE],
                               schema=NODE_SERVICE_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REFRESH_ENTITY,
                               async_refresh_entity,
                               descriptions[const.SERVICE_REFRESH_ENTITY],
                               schema=REFRESH_ENTITY_SCHEMA)
        hass.services.register(DOMAIN,
                               const.SERVICE_REFRESH_NODE,
                               refresh_node,
                               descriptions[const.SERVICE_REFRESH_NODE],
                               schema=NODE_SERVICE_SCHEMA)

    # Setup autoheal
    if autoheal:
        _LOGGER.info("ZWave network autoheal is enabled.")
        track_time_change(hass, heal_network, hour=0, minute=0, second=0)

    hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_zwave)

    if 'frontend' in hass.config.components:
        register_built_in_panel(hass, 'zwave', 'Z-Wave', 'mdi:nfc')

    return True
예제 #35
0
async def async_process_ha_core_config(
        hass: HomeAssistant,
        config: Dict,
        api_password: Optional[str] = None,
        trusted_networks: Optional[Any] = None) -> None:
    """Process the [homeassistant] section from the configuration.

    This method is a coroutine.
    """
    config = CORE_CONFIG_SCHEMA(config)

    # Only load auth during startup.
    if not hasattr(hass, 'auth'):
        auth_conf = config.get(CONF_AUTH_PROVIDERS)

        if auth_conf is None:
            auth_conf = [{'type': 'homeassistant'}]
            if api_password:
                auth_conf.append({
                    'type': 'legacy_api_password',
                    'api_password': api_password,
                })
            if trusted_networks:
                auth_conf.append({
                    'type': 'trusted_networks',
                    'trusted_networks': trusted_networks,
                })

        mfa_conf = config.get(CONF_AUTH_MFA_MODULES, [
            {
                'type': 'totp',
                'id': 'totp',
                'name': 'Authenticator app'
            },
        ])

        setattr(hass, 'auth', await
                auth.auth_manager_from_config(hass, auth_conf, mfa_conf))

    await hass.config.async_load()

    hac = hass.config

    if any([
            k in config for k in [
                CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, CONF_ELEVATION,
                CONF_TIME_ZONE, CONF_UNIT_SYSTEM
            ]
    ]):
        hac.config_source = SOURCE_YAML

    for key, attr in ((CONF_LATITUDE, 'latitude'), (CONF_LONGITUDE,
                                                    'longitude'),
                      (CONF_NAME, 'location_name'), (CONF_ELEVATION,
                                                     'elevation')):
        if key in config:
            setattr(hac, attr, config[key])

    if CONF_TIME_ZONE in config:
        hac.set_time_zone(config[CONF_TIME_ZONE])

    # Init whitelist external dir
    hac.whitelist_external_dirs = {hass.config.path('www')}
    if CONF_WHITELIST_EXTERNAL_DIRS in config:
        hac.whitelist_external_dirs.update(
            set(config[CONF_WHITELIST_EXTERNAL_DIRS]))

    # Customize
    cust_exact = dict(config[CONF_CUSTOMIZE])
    cust_domain = dict(config[CONF_CUSTOMIZE_DOMAIN])
    cust_glob = OrderedDict(config[CONF_CUSTOMIZE_GLOB])

    for name, pkg in config[CONF_PACKAGES].items():
        pkg_cust = pkg.get(CONF_CORE)

        if pkg_cust is None:
            continue

        try:
            pkg_cust = CUSTOMIZE_CONFIG_SCHEMA(pkg_cust)
        except vol.Invalid:
            _LOGGER.warning("Package %s contains invalid customize", name)
            continue

        cust_exact.update(pkg_cust[CONF_CUSTOMIZE])
        cust_domain.update(pkg_cust[CONF_CUSTOMIZE_DOMAIN])
        cust_glob.update(pkg_cust[CONF_CUSTOMIZE_GLOB])

    hass.data[DATA_CUSTOMIZE] = \
        EntityValues(cust_exact, cust_domain, cust_glob)

    if CONF_UNIT_SYSTEM in config:
        if config[CONF_UNIT_SYSTEM] == CONF_UNIT_SYSTEM_IMPERIAL:
            hac.units = IMPERIAL_SYSTEM
        else:
            hac.units = METRIC_SYSTEM
    elif CONF_TEMPERATURE_UNIT in config:
        unit = config[CONF_TEMPERATURE_UNIT]
        if unit == TEMP_CELSIUS:
            hac.units = METRIC_SYSTEM
        else:
            hac.units = IMPERIAL_SYSTEM
        _LOGGER.warning(
            "Found deprecated temperature unit in core "
            "configuration expected unit system. Replace '%s: %s' "
            "with '%s: %s'", CONF_TEMPERATURE_UNIT, unit, CONF_UNIT_SYSTEM,
            hac.units.name)
예제 #36
0
def setup(hass, config):
    """Set up the InfluxDB component."""
    from influxdb import InfluxDBClient, exceptions

    conf = config[DOMAIN]

    kwargs = {
        'database': conf[CONF_DB_NAME],
        'verify_ssl': conf[CONF_VERIFY_SSL],
        'timeout': TIMEOUT
    }

    if CONF_HOST in conf:
        kwargs['host'] = conf[CONF_HOST]

    if CONF_PORT in conf:
        kwargs['port'] = conf[CONF_PORT]

    if CONF_USERNAME in conf:
        kwargs['username'] = conf[CONF_USERNAME]

    if CONF_PASSWORD in conf:
        kwargs['password'] = conf[CONF_PASSWORD]

    if CONF_SSL in conf:
        kwargs['ssl'] = conf[CONF_SSL]

    include = conf.get(CONF_INCLUDE, {})
    exclude = conf.get(CONF_EXCLUDE, {})
    whitelist_e = set(include.get(CONF_ENTITIES, []))
    whitelist_d = set(include.get(CONF_DOMAINS, []))
    blacklist_e = set(exclude.get(CONF_ENTITIES, []))
    blacklist_d = set(exclude.get(CONF_DOMAINS, []))
    tags = conf.get(CONF_TAGS)
    tags_attributes = conf.get(CONF_TAGS_ATTRIBUTES)
    default_measurement = conf.get(CONF_DEFAULT_MEASUREMENT)
    override_measurement = conf.get(CONF_OVERRIDE_MEASUREMENT)
    component_config = EntityValues(conf[CONF_COMPONENT_CONFIG],
                                    conf[CONF_COMPONENT_CONFIG_DOMAIN],
                                    conf[CONF_COMPONENT_CONFIG_GLOB])
    max_tries = conf.get(CONF_RETRY_COUNT)

    try:
        influx = InfluxDBClient(**kwargs)
        influx.query("SHOW SERIES LIMIT 1;", database=conf[CONF_DB_NAME])
    except (exceptions.InfluxDBClientError,
            requests.exceptions.ConnectionError) as exc:
        _LOGGER.error(
            "Database host is not accessible due to '%s', please "
            "check your entries in the configuration file (host, "
            "port, etc.) and verify that the database exists and is "
            "READ/WRITE", exc)
        return False

    def event_to_json(event):
        """Add an event to the outgoing Influx list."""
        state = event.data.get('new_state')
        if state is None or state.state in (
                STATE_UNKNOWN, '', STATE_UNAVAILABLE) or \
                state.entity_id in blacklist_e or state.domain in blacklist_d:
            return

        try:
            if (whitelist_e and state.entity_id not in whitelist_e) or \
                    (whitelist_d and state.domain not in whitelist_d):
                return

            _include_state = _include_value = False

            _state_as_value = float(state.state)
            _include_value = True
        except ValueError:
            try:
                _state_as_value = float(state_helper.state_as_number(state))
                _include_state = _include_value = True
            except ValueError:
                _include_state = True

        include_uom = True
        measurement = component_config.get(
            state.entity_id).get(CONF_OVERRIDE_MEASUREMENT)
        if measurement in (None, ''):
            if override_measurement:
                measurement = override_measurement
            else:
                measurement = state.attributes.get('unit_of_measurement')
                if measurement in (None, ''):
                    if default_measurement:
                        measurement = default_measurement
                    else:
                        measurement = state.entity_id
                else:
                    include_uom = False

        json = {
            'measurement': measurement,
            'tags': {
                'domain': state.domain,
                'entity_id': state.object_id,
            },
            'time': event.time_fired,
            'fields': {}
        }
        if _include_state:
            json['fields']['state'] = state.state
        if _include_value:
            json['fields']['value'] = _state_as_value

        for key, value in state.attributes.items():
            if key in tags_attributes:
                json['tags'][key] = value
            elif key != 'unit_of_measurement' or include_uom:
                # If the key is already in fields
                if key in json['fields']:
                    key = key + "_"
                # Prevent column data errors in influxDB.
                # For each value we try to cast it as float
                # But if we can not do it we store the value
                # as string add "_str" postfix to the field key
                try:
                    json['fields'][key] = float(value)
                except (ValueError, TypeError):
                    new_key = "{}_str".format(key)
                    new_value = str(value)
                    json['fields'][new_key] = new_value

                    if RE_DIGIT_TAIL.match(new_value):
                        json['fields'][key] = float(
                            RE_DECIMAL.sub('', new_value))

                # Infinity and NaN are not valid floats in InfluxDB
                try:
                    if not math.isfinite(json['fields'][key]):
                        del json['fields'][key]
                except (KeyError, TypeError):
                    pass

        json['tags'].update(tags)

        return json

    instance = hass.data[DOMAIN] = InfluxThread(hass, influx, event_to_json,
                                                max_tries)
    instance.start()

    def shutdown(event):
        """Shut down the thread."""
        instance.queue.put(None)
        instance.join()
        influx.close()

    hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, shutdown)

    return True