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'}
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_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_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_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', }
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'}
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"}
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
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', }
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)
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", }
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"}
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))
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
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'}
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))
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'}
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
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'}
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
def test_override_by_domain(): """Test values with domain match.""" store = EV(domain={'test': {'key': 'value'}}) assert store.get(ent) == {'key': 'value'}
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, )
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,
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
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
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
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
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)
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