async def test_as_number_coercion(opp): """Test state_as_number with number.""" for _state in ("0", "0.0", 0, 0.0): assert state.state_as_number(ha.State("domain.test", _state, {})) == 0.0 for _state in ("1", "1.0", 1, 1.0): assert state.state_as_number(ha.State("domain.test", _state, {})) == 1.0
async def test_as_number_states(opp): """Test state_as_number with states.""" zero_states = ( STATE_OFF, STATE_CLOSED, STATE_UNLOCKED, STATE_BELOW_HORIZON, STATE_NOT_HOME, ) one_states = (STATE_ON, STATE_OPEN, STATE_LOCKED, STATE_ABOVE_HORIZON, STATE_HOME) for _state in zero_states: assert state.state_as_number(ha.State("domain.test", _state, {})) == 0 for _state in one_states: assert state.state_as_number(ha.State("domain.test", _state, {})) == 1
def state_changed_listener(event): """Listen for new messages on the bus and sends them to Datadog.""" state = event.data.get("new_state") if state is None or state.state == STATE_UNKNOWN: return states = dict(state.attributes) metric = f"{prefix}.{state.domain}" tags = [f"entity:{state.entity_id}"] for key, value in states.items(): if isinstance(value, (float, int)): attribute = f"{metric}.{key.replace(' ', '_')}" value = int(value) if isinstance(value, bool) else value statsd.gauge(attribute, value, sample_rate=sample_rate, tags=tags) _LOGGER.debug("Sent metric %s: %s (tags: %s)", attribute, value, tags) try: value = state_helper.state_as_number(state) except ValueError: _LOGGER.debug("Error sending %s: %s (tags: %s)", metric, state.state, tags) return statsd.gauge(metric, value, sample_rate=sample_rate, tags=tags) _LOGGER.debug("Sent metric %s: %s (tags: %s)", metric, value, tags)
def update_emoncms(time): """Send whitelisted entities states regularly to Emoncms.""" payload_dict = {} for entity_id in whitelist: state = opp.states.get(entity_id) if state is None or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE): continue try: payload_dict[entity_id] = state_helper.state_as_number(state) except ValueError: continue if payload_dict: payload = "{%s}" % ",".join(f"{key}:{val}" for key, val in payload_dict.items()) send_data( conf.get(CONF_URL), conf.get(CONF_API_KEY), str(conf.get(CONF_INPUTNODE)), payload, ) track_point_in_time( opp, update_emoncms, time + timedelta(seconds=conf.get(CONF_SCAN_INTERVAL)))
def state_as_number(state): """Return a state casted to a float.""" try: value = state_helper.state_as_number(state) except ValueError: _LOGGER.debug("Could not convert %s to float", state) value = 0 return value
def event_to_metrics(event, float_keys, string_keys): """Add an event to the outgoing Zabbix list.""" state = event.data.get("new_state") if state is None or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE): return entity_id = state.entity_id if not entities_filter(entity_id): return floats = {} strings = {} try: _state_as_value = float(state.state) floats[entity_id] = _state_as_value except ValueError: try: _state_as_value = float(state_helper.state_as_number(state)) floats[entity_id] = _state_as_value except ValueError: strings[entity_id] = state.state for key, value in state.attributes.items(): # For each value we try to cast it as float # But if we can not do it we store the value # as string attribute_id = f"{entity_id}/{key}" try: float_value = float(value) except (ValueError, TypeError): float_value = None if float_value is None or not math.isfinite(float_value): strings[attribute_id] = str(value) else: floats[attribute_id] = float_value metrics = [] float_keys_count = len(float_keys) float_keys.update(floats) if len(float_keys) != float_keys_count: floats_discovery = [] for float_key in float_keys: floats_discovery.append({"{#KEY}": float_key}) metric = ZabbixMetric( publish_states_host, "openpeerpower.floats_discovery", json.dumps(floats_discovery), ) metrics.append(metric) for key, value in floats.items(): metric = ZabbixMetric(publish_states_host, f"openpeerpower.float[{key}]", value) metrics.append(metric) string_keys.update(strings) return metrics
def event_to_json(event): """Add an event to the outgoing list.""" state = event.data.get("new_state") if (state is None or state.state in (STATE_UNKNOWN, "", STATE_UNAVAILABLE) or state.entity_id in exclude_e or state.domain in exclude_d): return if (include_e and state.entity_id not in include_e) or ( include_d and state.domain not in include_d): return try: _state_as_value = float(state.state) except ValueError: _state_as_value = None if _state_as_value is None: try: _state_as_value = float(state_helper.state_as_number(state)) except ValueError: _state_as_value = None out_event = { "tags": { "domain": state.domain, "entity_id": state.object_id }, "time": event.time_fired.isoformat(), "fields": { "state": state.state }, } if _state_as_value is not None: out_event["fields"]["state_value"] = _state_as_value for key, value in state.attributes.items(): if key != "unit_of_measurement": # If the key is already in fields if key in out_event["fields"]: key = f"{key}_" # For each value we try to cast it as float # But if we can not do it we store the value # as string try: out_event["fields"][key] = float(value) except (ValueError, TypeError): out_event["fields"][key] = str(value) return out_event
def dweet_event_listener(event): """Listen for new messages on the bus and sends them to Dweet.io.""" state = event.data.get("new_state") if ( state is None or state.state in (STATE_UNKNOWN, "") or state.entity_id not in whitelist ): return try: _state = state_helper.state_as_number(state) except ValueError: _state = state.state json_body[state.attributes.get(ATTR_FRIENDLY_NAME)] = _state send_data(name, json_body)
def thingspeak_listener(entity_id, old_state, new_state): """Listen for new events and send them to Thingspeak.""" if new_state is None or new_state.state in ( STATE_UNKNOWN, "", STATE_UNAVAILABLE, ): return try: if new_state.entity_id != entity: return _state = state_helper.state_as_number(new_state) except ValueError: return try: channel.update({"field1": _state}) except RequestException: _LOGGER.error("Error while sending value '%s' to Thingspeak", _state)
def _report_attributes(self, entity_id, new_state): """Report the attributes.""" now = time.time() things = dict(new_state.attributes) with suppress(ValueError): things["state"] = state.state_as_number(new_state) lines = [ "%s.%s.%s %f %i" % (self._prefix, entity_id, key.replace(" ", "_"), value, now) for key, value in things.items() if isinstance(value, (float, int)) ] if not lines: return _LOGGER.debug("Sending to graphite: %s", lines) try: self._send_to_graphite("\n".join(lines)) except socket.gaierror: _LOGGER.error("Unable to connect to host %s", self._host) except OSError: _LOGGER.exception("Failed to send data to graphite")
def logentries_event_listener(event): """Listen for new messages on the bus and sends them to Logentries.""" state = event.data.get("new_state") if state is None: return try: _state = state_helper.state_as_number(state) except ValueError: _state = state.state json_body = [{ "domain": state.domain, "entity_id": state.object_id, "attributes": dict(state.attributes), "time": str(event.time_fired), "value": _state, }] try: payload = {"host": le_wh, "event": json_body} requests.post(le_wh, data=json.dumps(payload), timeout=10) except requests.exceptions.RequestException as error: _LOGGER.exception("Error sending to Logentries: %s", error)
def statsd_event_listener(event): """Listen for new messages on the bus and sends them to StatsD.""" state = event.data.get("new_state") if state is None: return try: if value_mapping and state.state in value_mapping: _state = float(value_mapping[state.state]) else: _state = state_helper.state_as_number(state) except ValueError: # Set the state to none and continue for any numeric attributes. _state = None states = dict(state.attributes) _LOGGER.debug("Sending %s", state.entity_id) if show_attribute_flag is True: if isinstance(_state, (float, int)): statsd_client.gauge("%s.state" % state.entity_id, _state, sample_rate) # Send attribute values for key, value in states.items(): if isinstance(value, (float, int)): stat = "{}.{}".format(state.entity_id, key.replace(" ", "_")) statsd_client.gauge(stat, value, sample_rate) else: if isinstance(_state, (float, int)): statsd_client.gauge(state.entity_id, _state, sample_rate) # Increment the count statsd_client.incr(state.entity_id, rate=sample_rate)
async def test_as_number_invalid_cases(opp): """Test state_as_number with invalid cases.""" for _state in ("", "foo", "foo.bar", None, False, True, object, object()): with pytest.raises(ValueError): state.state_as_number(ha.State("domain.test", _state, {}))
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 with suppress(KeyError, TypeError): if not math.isfinite(json[INFLUX_CONF_FIELDS][key]): del json[INFLUX_CONF_FIELDS][key] json[INFLUX_CONF_TAGS].update(tags) return json