def rule_openhab_sensor(obj=m.Sensor(), change=None): if obj.sensor_name is None: L.l.warning('Got empty openhab sensor name {}'.format(obj)) return key = 'temperature' if hasattr(obj, key) and obj.temperature is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.temperature) key = 'humidity' if hasattr(obj, key) and obj.humidity is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.humidity) key = 'pressure' if hasattr(obj, key) and obj.pressure is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.pressure) key = 'vad' if hasattr(obj, key) and obj.vad is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.vad) key = 'vdd' if hasattr(obj, key) and obj.vdd is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.vdd) key = 'iad' if hasattr(obj, key) and obj.iad is not None: send_mqtt_openhab(subtopic=key + "_" + obj.sensor_name, payload=obj.iad)
def __save_sensor_db(p_id='', p_type='', value_list=None): if not value_list: value_list = [] record = m.Sensor.find_one({m.Sensor.address: p_id}) if record is None: record = m.Sensor() record.address = p_id zone_sensor = m.ZoneSensor.find_one({m.ZoneSensor.sensor_address: p_id}) if zone_sensor: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = '(not defined) ' + p_id record.updated_on = utils.get_base_location_now_date() record.type = p_type if 'Humidity' in value_list: record.humidity = utils.round_sensor_value(value_list['Humidity']) if 'Temperature' in value_list: record.temperature = utils.round_sensor_value( value_list['Temperature']) if 'Battery numeric' in value_list: record.battery_level = value_list['Battery numeric'] if 'Rssi numeric' in value_list: record.rssi = value_list['Rssi numeric'] # L.l.info('Saving RFX object {}'.format(record)) record.save_changed_fields(broadcast=True, persist=True)
def _get_sensor(sensor_address, sensor_type): zone_sensor, actual_sensor_name = _get_zone_sensor(sensor_address, sensor_type) sensor = m.Sensor.find_one({m.Sensor.address: sensor_address}) if sensor is None: sensor = m.Sensor() sensor.address = sensor_address sensor.sensor_name = actual_sensor_name return zone_sensor, sensor
def rule_sensor_temp_extreme(obj=m.Sensor(), change=None): if hasattr(obj, 'temperature') and obj.temperature is not None: zonesensor = m.ZoneSensor.find_one( {m.ZoneSensor.sensor_name: obj.sensor_name}) if zonesensor is not None and zonesensor.target_material is not None: zone = m.Zone.find_one({m.Zone.id: zonesensor.zone_id}) if zone is not None: max = min = None if zone.is_indoor: location = 'indoor' elif zone.is_indoor_heated: location = 'indoor_heated' elif zone.is_outdoor: location = 'outdoor' elif zone.is_outdoor_heated: location = 'outdoor_heated' else: L.l.warning( "Zone {} has no indoor/outdoor location set".format( zone.name)) return False max_temp = TempStore.max_temp[location] if zonesensor.target_material in max_temp: max = max_temp[zonesensor.target_material] else: L.l.warning("Unknown max target material {}".format( zonesensor.target_material)) min_temp = TempStore.min_temp[location] if zonesensor.target_material in min_temp: min = min_temp[zonesensor.target_material] else: L.l.warning("Unknown min target material {}".format( zonesensor.target_material)) if max is not None and obj.temperature >= max: rule_common.notify_via_all( title="Max temperature reached for {} is {}".format( obj.sensor_name, obj.temperature), message="!", priority=1) if min is not None and obj.temperature <= min: rule_common.notify_via_all( title="Min temperature reached for {} is {}".format( obj.sensor_name, obj.temperature), message="!", priority=1) else: L.l.warning( "Cannot find a zone for zone_sensor {}".format(zonesensor)) return True
def set_value(network, node, value): try: # L.l.info('Louie set_value signal: Node={} Value={}'.format(node, value)) sensor_address = "{}_{}".format(node.product_name, node.node_id) zone_sensor = m.ZoneSensor.find_one( {m.ZoneSensor.sensor_address: sensor_address}) if zone_sensor is not None: sensor_name = zone_sensor.sensor_name P.last_value_received = datetime.now() if value.label == "Switch": _set_custom_relay_state(sensor_address=sensor_address, state=value.data) elif value.label == "Power": if value.units == "W": units_adjusted = "watt" # this should match Utility unit name in models definition value_adjusted = round(value.data, 0) if abs(value_adjusted) < P.MAX_SENSOR_POWER_WATTS: haiot_dispatch.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=value_adjusted, unit=units_adjusted) else: L.l.warning( "Faulty power reading, value={}, cap={}".format( value_adjusted, P.MAX_SENSOR_POWER_WATTS)) else: L.l.warning("Unknown power units {} for sensor {}".format( value.units, sensor_name)) # L.l.info("Saving power utility {} {} {}".format(sensor_name, value.units, value.data)) elif value.label == "Energy" and value.units == "kWh": haiot_dispatch.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=value.data, unit=value.units) # L.l.info("Saving energy utility {} {} {}".format(sensor_name, value.units, value.data)) else: # skip controller node if node.node_id > 1: record = m.Sensor.find_one( {m.Sensor.address: sensor_address}) delta_last_save = P.DELTA_SAVE_SECONDS if record is None: L.l.info("Zwave sensor address not found:[{}]".format( sensor_address)) record = m.Sensor() record.address = sensor_address record.sensor_name = zone_sensor.sensor_name record.updated_on = datetime.now() else: if record.updated_on is not None: delta_last_save = ( datetime.now() - record.updated_on).total_seconds() record.is_event_external = True if value.label == "Voltage": if abs(value.data) < P.MAX_SENSOR_VOLTAGE: record.vad = round(value.data, 0) record.save_changed_fields(broadcast=False, persist=True) else: L.l.warning( "Faulty voltage reading, value={}, cap={}". format(value.data, P.MAX_SENSOR_VOLTAGE)) # L.l.info("Saving voltage {} {}".format(sensor_name, value.data)) elif value.label == "Current": record.iad = round(value.data, 1) record.save_changed_fields(broadcast=False, persist=True) # L.l.info("Saving current {} {}".format(sensor_name, value.data)) elif value.label == "Power Factor": if abs(value.data) < P.MAX_POWER_FACTOR: record.vdd = round(value.data, 1) record.save_changed_fields(broadcast=False, persist=True) else: L.l.warning( "Faulty pf reading, value={}, cap={}".format( value.data, P.MAX_POWER_FACTOR)) # L.l.info("Saving power factor {} {}".format(sensor_name, value.data)) else: # L.l.warning("Doing nothing on zwave set value {}".format(value)) pass else: L.l.info( "Cannot find zwave sensor in db, address={} node={} value={}". format(sensor_address, node, value)) except Exception as ex: L.l.error("Error in zwave value={}".format(ex), exc_info=True)
def rule_sensor_temp_target(obj=m.Sensor(), change=None): if not change: field_changed_list = [] #temp = obj.temperature return 'rule temp ok'
def save_to_db(dev): try: delta_time_counters = None address = dev['address'] zone_sensor = m.ZoneSensor.find_one( {m.ZoneSensor.sensor_address: address}) record = m.Sensor.find_one({m.Sensor.address: address}) if record is None: record = m.Sensor() record.address = address if zone_sensor: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = '{} {} {}'.format(address, dev['n_address'], dev['type']) record.type = dev['type'] record.updated_on = utils.get_base_location_now_date() record.comment = dev['path'] if 'counters_a' in dev.keys(): if record.counters_a is not None: record.delta_counters_a = dev['counters_a'] - record.counters_a else: record.delta_counters_a = 0 # don't know prev. count, assume no consumption (ticks could be lost) if record.updated_on is not None: # get accurate interval (e.g. to establish power consumption in watts) delta_time_counters = (utils.get_base_location_now_date() - record.updated_on).total_seconds() else: # when running first time with db empty delta_time_counters = P.sampling_period_seconds if 'counters_b' in dev.keys(): if record.counters_b is not None: record.delta_counters_b = dev['counters_b'] - record.counters_b else: # fixme: don't know prev. count, assume no consumption (ticks could be lost) record.delta_counters_b = 0 if 'temperature' in dev.keys(): record.temperature = dev['temperature'] if 'humidity' in dev.keys(): record.humidity = dev['humidity'] if 'iad' in dev.keys(): record.iad = dev['iad'] if 'vad' in dev.keys(): record.vad = dev['vad'] if 'vdd' in dev.keys(): record.vdd = dev['vdd'] if 'pio_a' in dev.keys(): record.pio_a = dev['pio_a'] if 'pio_b' in dev.keys(): record.pio_b = dev['pio_b'] if 'sensed_a' in dev.keys(): record.sensed_a = dev['sensed_a'] if 'sensed_b' in dev.keys(): record.sensed_b = dev['sensed_b'] # force field changed detection for delta_counters to enable save in history # but allow one 0 record to be saved for nicer graphics record.save_changed_fields(broadcast=False, persist=True) if record.delta_counters_a is not None or record.delta_counters_b is not None: dispatcher.send(Constant.SIGNAL_UTILITY, sensor_name=record.sensor_name, units_delta_a=record.delta_counters_a, units_delta_b=record.delta_counters_b, total_units_a=record.counters_a, total_units_b=record.counters_b, sampling_period_seconds=delta_time_counters) if record.vad is not None: dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=record.sensor_name, value=record.vad) except Exception as ex: L.l.error('Error saving sensor to DB, err {}'.format(ex), exc_info=True)
def thread_run(): if not P.initialised: init_solar_aps() if P.initialised: try: aps_text = str( urllib.request.urlopen( get_json_param(Constant.P_SOLAR_APS_LOCAL_URL)).read()) production = utils.parse_text(aps_text, P.start_keyword, P.end_keyword) last_power = utils.parse_text(aps_text, P.start_keyword_now, P.end_keyword_now) temperature = utils.parse_text(aps_text, P.start_key_temp, P.end_key_temp, end_first=True) panel_id = utils.parse_text(aps_text, P.start_key_panel, P.end_key_panel, end_first=True) utility_name = get_json_param(Constant.P_SOLAR_UTILITY_NAME) if temperature is not None: zone_sensor = m.ZoneSensor.find_one( {m.ZoneSensor.sensor_address: panel_id}) if zone_sensor is None: L.l.warning( 'Solar panel id {} is not defined in zone sensor list'. format(panel_id)) record = m.Sensor.find_one({m.Sensor.address: panel_id}) if record is None: record = m.Sensor() record.address = panel_id record.type = 'solar' if zone_sensor: record.sensor_name = zone_sensor.sensor_name else: record.sensor_name = record.type + panel_id record.temperature = temperature record.updated_on = utils.get_base_location_now_date() # fixme: keeps saving same temp even when panels are off. stop during night. record.save_changed_fields(broadcast=True, persist=True) if production is not None: production = float(production) record = m.Utility.find_one( {m.Utility.utility_name: utility_name}) if record is None: record = m.Utility() record.utility_name = utility_name record.units_delta = production record.units_total = production else: if record.units_total is None: record.units_total = production record.units_delta = 0 else: record.units_delta = production - record.units_total if record.units_delta == 0: # do not waste db space if no power generated L.l.info( 'Solar production is energy={} watts={} temp={}' .format(production, last_power, temperature)) # return else: L.l.info('Solar production is {}'.format( record.units_delta)) record.units_2_delta = last_power # L.l.info('Solar watts is {}'.format(last_power)) if record.unit_cost is None: record.unit_cost = 0.0 record.cost = 1.0 * record.units_delta * record.unit_cost record.save_changed_fields(broadcast=True, persist=True) else: L.l.info('Solar production is none') except Exception as ex: L.l.error("Got exception on solar thread run, ex={}".format(ex), exc_info=True)
def _process_message(msg): # L.l.info("Topic={} payload={}".format(msg.topic, msg.payload)) if '/SENSOR' in msg.topic or '/RESULT' in msg.topic: topic_clean = P.sonoff_topic.replace('#', '') if topic_clean in msg.topic: try: sensor_name = msg.topic.split(topic_clean)[1].split('/')[1] obj = utils.json2obj(transport.mqtt_io.payload2json(msg.payload)) except Exception as ex: L.l.error("Error decoding sensor name, topic={} payload={}".format(msg.topic, msg.payload)) return False if obj is None: L.l.error("Unable to decode tasmota {}".format(msg.payload)) return False ################################################################# if 'ENERGY' in obj: energy = obj['ENERGY'] power = float(energy['Power']) if 'Voltage' in energy: voltage = int(energy['Voltage']) else: voltage = None if 'Factor' in energy: factor = energy['Factor'] else: factor = None if 'Current' in energy: current = float(energy['Current']) else: current = None if 'Today' in energy: today_energy = energy['Today'] else: today_energy = None # unit should match Utility unit name in models definition dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=power, unit='watt') # todo: save total energy utility if voltage or factor or current: zone_sensor = m.ZoneSensor.find_one({m.ZoneSensor.sensor_address: sensor_name}) if zone_sensor is not None: record = m.Sensor.find_one({m.Sensor.address: sensor_name}) if record is None: record = m.Sensor() record.address = sensor_name record.sensor_name = zone_sensor.sensor_name record.vad = None record.iad = None record.vdd = None if voltage is not None: record.vad = round(voltage, 0) if current is not None: record.iad = round(current, 1) if factor is not None: record.vdd = round(factor, 1) if voltage is not None or current is not None or factor is not None: record.save_changed_fields(broadcast=False, persist=True) # dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=current, unit='kWh') # check for single relay if 'POWER' in obj: power_is_on = obj['POWER'] == 'ON' # relay = m.ZoneCustomRelay.find_one({m.ZoneCustomRelay.gpio_pin_code: sensor_name, # m.ZoneCustomRelay.gpio_host_name: Constant.HOST_NAME}) relay = m.ZoneCustomRelay.find_one({m.ZoneCustomRelay.gpio_pin_code: sensor_name}) if relay is not None: L.l.info("Got single relay {} state={}".format(sensor_name, power_is_on)) relay.relay_is_on = power_is_on # set listeners to false as otherwise on reboot sonoff relays that were on will power- cycle relay.save_changed_fields(broadcast=False, persist=True, listeners=False) else: L.l.warning("ZoneCustomRelay single {} not defined in db".format(sensor_name)) # check for multiple relays multiple_relays_list = [] if 'POWER1' in obj: multiple_relays_list.append(1) if 'POWER2' in obj: multiple_relays_list.append(2) if len(multiple_relays_list) > 0: for index in multiple_relays_list: power_is_on = obj['POWER' + str(index)] == 'ON' relay = m.ZoneCustomRelay.find_one({m.ZoneCustomRelay.gpio_pin_code: sensor_name, m.ZoneCustomRelay.relay_index: index}) if relay is not None: L.l.info("Got multiple relay {} state={}".format(sensor_name, power_is_on)) relay.relay_is_on = power_is_on # set listeners to false as otherwise on reboot sonoff relays that were on will power- cycle relay.save_changed_fields(broadcast=False, persist=True, listeners=False) else: L.l.warning("ZoneCustomRelay multiple {} not defined in db".format(sensor_name)) if 'COUNTER' in obj: # TelePeriod 60 counter = obj['COUNTER'] for i in [1, 2, 3, 4, 5, 6, 7, 8]: c = 'C{}'.format(i) if c in counter: cval = int(counter[c]) dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=cval, index=i) # iot/sonoff/tele/sonoff-basic-3/SENSOR = # {"Time":"2018-10-28T08:12:26","BMP280":{"Temperature":24.6,"Pressure":971.0},"TempUnit":"C"} # "BME280":{"Temperature":24.1,"Humidity":39.2,"Pressure":980.0},"PressureUnit":"hPa","TempUnit":"C"} # {"BME680":{"Temperature":29.0,"Humidity":63.3,"Pressure":981.6,"Gas":24.46},"PressureUnit":"hPa","TempUnit":"C"} # "MHZ19B":{"Model":"B","CarbonDioxide":473,"Temperature":26.0},"TempUnit":"C" for k, v in obj.items(): if k.startswith('BME') or k.startswith('BMP') or k.startswith('MHZ19') or k.startswith('DS18B20'): sensor_address = '{}_{}'.format(sensor_name, k.lower()) zone_sensor, sensor = _get_air_sensor(sensor_address=sensor_address, sensor_type=k) if 'Temperature' in v: sensor.temperature = v['Temperature'] if 'Pressure' in v: sensor.pressure = v['Pressure'] if 'Humidity' in v: if 0 < v['Humidity'] < 100: sensor.humidity = v['Humidity'] if 'Gas' in v: sensor.gas = v['Gas'] if 'CarbonDioxide' in v: val = v['CarbonDioxide'] if val > 0: sensor.co2 = val sensor.save_changed_fields(broadcast=False, persist=True) if k.startswith('INA219'): ina = v # obj['INA219'] # multiple ina sensors if '-' in k: index = k.split('-')[1] sensor = m.PowerMonitor.find_one( {m.PowerMonitor.host_name: sensor_name, m.PowerMonitor.type: "ina{}".format(index)}) else: sensor = m.PowerMonitor.find_one({m.PowerMonitor.host_name: sensor_name}) if sensor is None: L.l.warning('Sensor INA on {} not defined in db'.format(sensor_name)) else: if 'Voltage' in ina: voltage = ina['Voltage'] sensor.voltage = voltage if 'Current' in ina: current = ina['Current'] sensor.current = current if 'Power' in ina: power = ina['Power'] sensor.power = power sensor.save_changed_fields(broadcast=False, persist=True) if 'ANALOG' in obj: # "ANALOG":{"A0":7} an = obj['ANALOG'] a0 = an['A0'] sensor_address = '{}_{}'.format(sensor_name, 'a0') zone_sensor, sensor = _get_sensor(sensor_address=sensor_address, sensor_type='ANALOG') sensor.vad = a0 sensor.save_changed_fields(broadcast=False, persist=True) if 'PMS5003' in obj: # "PMS5003":{"CF1":0,"CF2.5":1,"CF10":3,"PM1":0,"PM2.5":1,"PM10":3,"PB0.3":444,"PB0.5":120,"PB1":12, # "PB2.5":6,"PB5":2,"PB10":2} pms = obj['PMS5003'] sensor_address = '{}_{}'.format(sensor_name, 'pms5003') zone_sensor, sensor = _get_dust_sensor(sensor_address=sensor_address, sensor_type='PMS5003') sensor.pm_1 = pms['PM1'] sensor.pm_2_5 = pms['PM2.5'] sensor.pm_10 = pms['PM10'] sensor.p_0_3 = pms['PB0.3'] sensor.p_0_5 = pms['PB0.5'] sensor.p_1 = pms['PB1'] sensor.p_2_5 = pms['PB2.5'] sensor.p_5 = pms['PB5'] sensor.p_10 = pms['PB10'] # sometimes first read after power on returns invalid 0 values if sensor.pm_1 + sensor.pm_2_5 + sensor.pm_10 + sensor.p_0_3 + sensor.p_0_5 + sensor.p_1 \ + sensor.p_2_5 + sensor.p_5 + sensor.p_10 != 0: sensor.save_changed_fields(broadcast=False, persist=True) if 'RfReceived' in obj: rf = obj['RfReceived'] sensor_id = rf['Data'] alarm = m.ZoneAlarm.find_one({m.ZoneAlarm.gpio_pin_code: sensor_id}) if alarm is not None: dispatcher.send(Constant.SIGNAL_GPIO, gpio_pin_code=sensor_id, pin_connected=False) dispatcher.send(Constant.SIGNAL_GPIO, gpio_pin_code=sensor_id, pin_connected=True) else: L.l.warning('Unknown Sonoff RF packet received {}'.format(sensor_id)) else: L.l.warning("Invalid sensor topic {}".format(msg.topic)) return True