Esempio n. 1
0
def handle_local_event_db_post(model,
                               row,
                               last_commit_field_changed_list=None):
    processed = False
    L.l.debug('Local DB change sent by model {} row {}'.format(model, row))
    if str(models.Parameter) in str(model):
        # fixme: update app if params are changing to avoid need for restart
        processed = True
    # no need to propagate changes to other nodes
    elif str(models.Module) in str(model):
        if row.host_name == Constant.HOST_NAME:
            main.init_module(row.name, row.active)
            processed = True

    # propagate changes to all nodes as each must execute db sync or other commands locally
    # add here tables you are sure are safe to be propagated to all nodes
    elif str(models.Node) in str(model) or str(models.GpioPin) in str(model) \
            or str(models.ZoneCustomRelay) in str(model) \
            or str(models.Rule) in str(model) \
            or str(models.ZoneThermostat) in str(model)\
            or str(models.Pwm) in str(model):
        # or str(models.Area) in str(model) :
        txt = model_helper.model_row_to_json(row, operation='update')
        # execute all events directly first, then broadcast, as local events are not handled by remote mqtt queue
        obj_json = utils.json2obj(txt)
        obj_json[Constant.
                 JSON_PUBLISH_FIELDS_CHANGED] = last_commit_field_changed_list
        # handle_event_mqtt_received(None, None, 'direct-event', obj_json)
        #_handle_internal_event(obj_json)
        _process_obj(obj_json)
        # mqtt_thread_run()
        transport.send_message_json(json=txt)
        processed = True
Esempio n. 2
0
def on_models_committed(sender, changes):
    try:
        for obj, change in changes:
            # avoid recursion
            if isinstance(obj, models.Pwm):
                L.l.info("Commit change PWM={}".format(obj))
            if hasattr(obj, Constant.JSON_PUBLISH_NOTIFY_TRANSPORT):
                # only send mqtt message once for db saves intended to be distributed
                if obj.notify_transport_enabled:
                    if hasattr(obj, Constant.JSON_PUBLISH_NOTIFY_DB_COMMIT):
                        if not obj.notified_on_db_commit:
                            obj.notified_on_db_commit = True
                            txt = model_helper.model_row_to_json(
                                obj, operation=change)
                            if txt is None:
                                txt = model_helper.model_row_to_json(
                                    obj, operation=change)
                                pass
                            # execute all events directly first,
                            # then broadcast, local events not handled by remote mqtt queue
                            _handle_internal_event(utils.json2obj(txt))
                            # _process_obj(utils.json2obj(txt))
                            transport.send_message_json(json=txt)
                else:
                    pass
            # send object to rule parser, if connected
            dispatcher.send(Constant.SIGNAL_DB_CHANGE_FOR_RULES,
                            obj=obj,
                            change=change)
    except Exception as ex:
        L.l.exception('Error in DB commit hook, {}'.format(ex))
Esempio n. 3
0
def handle_local_event_db_post(model, row):
    processed = False
    L.l.debug('Local DB change sent by model {} row {}'.format(model, row))
    if str(models.Parameter) in str(model):
        # fixme: update app if params are changing to avoid need for restart
        processed = True
    # no need to propagate changes to other nodes
    elif str(models.Module) in str(model):
        if row.host_name == Constant.HOST_NAME:
            main.init_module(row.name, row.active)
            processed = True

    # propagate changes to all nodes as each must execute db sync or other commands locally
    # add here tables you are sure are safe to be propagated to all nodes
    elif str(models.Node) in str(model) or str(models.GpioPin) in str(model) \
            or str(models.ZoneCustomRelay) in str(model) \
            or str(models.Rule) in str(model):  # or str(models.Area) in str(model):
        txt = model_helper.model_row_to_json(row, operation='update')
        # execute all events directly first, then broadcast, as local events are not handled by remote mqtt queue
        handle_event_mqtt_received(None, None, 'direct-event',
                                   utils.json2obj(txt))
        mqtt_thread_run()
        if transport.mqtt_io.client_connected:
            transport.send_message_json(json=txt)
        processed = True

    if processed:
        L.l.debug('Detected {} record change, row={}, trigger executed'.format(
            model, row))
    else:
        L.l.debug(
            'Detected {} record change, row={}, but change processing ignored'.
            format(model, row))
Esempio n. 4
0
def init():
    # use auto setup as described here: https://plot.ly/python/getting-started/ to avoid passwords in code
    # or less secure sign_in code below
    # py.sign_in(model_helper.get_param(constant.P_PLOTLY_USERNAME),model_helper.get_param(constant.P_PLOTLY_APIKEY))
    username = ""
    if py.get_credentials()['username'] == '' or py.get_credentials(
    )['api_key'] == '':
        env_var = 'PLOTLY_CREDENTIALS_PATH'
        alt_path = os.environ.get(env_var)
        if not alt_path:
            L.l.info(
                'Plotly config not in environment var: {}'.format(env_var))
            env_var = 'OPENSHIFT_REPO_DIR'
            alt_path = os.environ.get(env_var)
            if alt_path is None:
                L.l.info(
                    'Plotly config not in environment var: {}'.format(env_var))
                credential_file = model_helper.get_param(
                    Constant.P_PLOTLY_ALTERNATE_CONFIG)
                alt_path = os.getcwd() + '/' + credential_file
            else:
                L.l.info('Plotly config found in environment var: {}, path={}'.
                         format(env_var, alt_path))
                alt_path = str(alt_path) + '/../data/.plotly.credentials'
        L.l.info("Plotly standard config empty, trying alt_path={}".format(
            alt_path))
        try:
            with open(alt_path, 'r') as cred_file:
                data = cred_file.read().replace('\n', '')
            if len(data) > 0:
                cred_obj = utils.json2obj(data)
                username = cred_obj['username']
                api_key = cred_obj['api_key']
                if username and api_key:
                    py.sign_in(username, api_key)
                    global initialised
                    initialised = True
                    # else:
                    #    Log.logger.info("Plotly init from db folder config {}{} not ok, trying with db data".format(os.getcwd(),
                    #        credential_file))
                    #    #alternate way if reading data from DB
                    #    py.sign_in(model_helper.get_param(constant.P_PLOTLY_USERNAME),
                    #               model_helper.get_param(constant.P_PLOTLY_APIKEY))

        except Exception, ex:
            L.l.warning("error reading plotly credentials {}".format(ex))
Esempio n. 5
0
def on_message(client, userdata, msg):
    json = msg
    try:
        if utils.get_base_location_now_date().minute != P.last_minute:
            P.last_minute = utils.get_base_location_now_date().minute
            transport.mqtt_io.mqtt_msg_count_per_minute = 0
        transport.mqtt_io.mqtt_msg_count_per_minute += 1
        P.last_rec = utils.get_base_location_now_date()
        #L.l.debug('Received from client [{}] userdata [{}] msg [{}] at {} '.format(client._client_id,
        #                                                                           userdata, msg.topic,
        #                                                                           utils.get_base_location_now_date()))
        # locate json string
        start = msg.payload.find('{')
        end = msg.payload.find('}')
        json = msg.payload[start:end + 1]
        if '"source_host_": "{}"'.format(Constant.HOST_NAME) not in json:
            # ignore messages send by this host
            x = json2obj(json)
            #if x[Constant.JSON_PUBLISH_SOURCE_HOST] != str(Constant.HOST_NAME):
            start = utils.get_base_location_now_date()
            dispatcher.send(signal=Constant.SIGNAL_MQTT_RECEIVED,
                            client=client,
                            userdata=userdata,
                            topic=msg.topic,
                            obj=x)
            elapsed = (utils.get_base_location_now_date() -
                       start).total_seconds()
            if elapsed > 5:
                L.l.warning('Command received took {} seconds'.format(elapsed))
            if False:
                if hasattr(x, 'command') and hasattr(
                        x, 'command_id') and hasattr(x, 'host_target'):
                    if x.host_target == Constant.HOST_NAME:
                        L.l.info('Executing command {}'.format(x.command))
                    else:
                        L.l.info(
                            "Received command {} for other host {}".format(
                                x, x.host_target))
    except AttributeError as ex:
        L.l.warning('Unknown attribute error in msg {} err {}'.format(
            json, ex))
    except ValueError as e:
        L.l.warning('Invalid JSON {} {}'.format(json, e))
Esempio n. 6
0
def on_message(client, userdata, msg):
    if threading.current_thread().name != 'mqtt_loop':
        prctl.set_name("mqtt_loop")
        threading.current_thread().name = "mqtt_loop"
    start = utils.get_base_location_now_date()
    json = msg
    try:
        if utils.get_base_location_now_date().minute != P.last_minute:
            P.last_minute = utils.get_base_location_now_date().minute
            P.mqtt_msg_count_per_minute = 0
        P.mqtt_msg_count_per_minute += 1
        P.last_rec = utils.get_base_location_now_date()
        json = payload2json(msg.payload)
        # ignore messages sent by this host
        if '"source_host_": "{}"'.format(Constant.HOST_NAME) not in json \
                and '"source_host": "{}"'.format(Constant.HOST_NAME) not in json \
                and len(json) > 0:  # or Constant.HOST_NAME == 'netbook': #debug
            x = utils.json2obj(json)
            if '_sent_on' in x:
                delta = (start -
                         utils.parse_to_date(x['_sent_on'])).total_seconds()
                # L.l.info('Mqtt age={}'.format(delta))
                if delta > 5:
                    if 'source_host_' in x:
                        host = x['source_host_']
                    else:
                        host = 'N/A'
                    L.l.info('Mqtt delta {} source_host='.format(delta, host))
            x['is_event_external'] = True
            P.received_mqtt_list.append(x)
        else:
            pass
    except Exception as ex:
        L.l.warning('Unknown attribute error in msg {} err {}'.format(
            json, ex))
    prctl.set_name("idle_mqtt_loop")
    threading.current_thread().name = "idle_mqtt_loop"
Esempio n. 7
0
def mqtt_on_message(client, userdata, 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:
            sensor_name = msg.topic.split(topic_clean)[1].split('/')[1]
            obj = utils.json2obj(msg.payload)
            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
                # 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 = models.ZoneSensor.query.filter_by(sensor_address=sensor_name).first()
                    if zone_sensor is not None:
                        current_record = models.Sensor.query.filter_by(address=sensor_name).first()
                        if current_record is None:
                            pass
                        else:
                            current_record.vad = None
                            current_record.iad = None
                            current_record.vdd = None
                        record = models.Sensor(address=sensor_name, sensor_name=zone_sensor.sensor_name)
                        record.is_event_external = True
                        if voltage is not None:
                            record.vad = round(voltage, 0)
                            record.save_changed_fields(current_record=current_record, new_record=record,
                                                       notify_transport_enabled=True, save_to_graph=True, debug=False)
                        if current is not None:
                            record.iad = round(current, 1)
                            record.save_changed_fields(current_record=current_record, new_record=record,
                                                       notify_transport_enabled=True, save_to_graph=True, debug=False)
                        if factor is not None:
                            record.vdd = round(factor, 1)
                            record.save_changed_fields(current_record=current_record, new_record=record,
                                                       notify_transport_enabled=True, save_to_graph=True, debug=False)
                # dispatcher.send(Constant.SIGNAL_UTILITY_EX, sensor_name=sensor_name, value=current, unit='kWh')
            elif 'POWER' in obj:
                power_is_on = obj['POWER'] == 'ON'
                current_relay = models.ZoneCustomRelay.query.filter_by(gpio_pin_code=sensor_name,
                                                                       gpio_host_name=Constant.HOST_NAME).first()
                if current_relay is not None:
                    L.l.info("Got relay {} state={}".format(sensor_name, power_is_on))
                    new_relay = models.ZoneCustomRelay(gpio_pin_code=sensor_name, gpio_host_name=Constant.HOST_NAME)
                    new_relay.relay_is_on = power_is_on
                    current_relay.is_event_external = True
                    models.ZoneCustomRelay().save_changed_fields(current_record=current_relay, new_record=new_relay,
                                                                 notify_transport_enabled=True, save_to_graph=True)
                else:
                    L.l.error("ZoneCustomRelay with code {} does not exist in database".format(sensor_name))
            elif 'COUNTER' in obj:
                # TelePeriod 60
                counter = obj['COUNTER']
                for i in [1, 2, 3, 4]:
                    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)
            elif 'BMP280' in obj:
                # iot/sonoff/tele/sonoff-basic-3/SENSOR =
                # {"Time":"2018-10-28T08:12:26","BMP280":{"Temperature":24.6,"Pressure":971.0},"TempUnit":"C"}
                bmp = obj['BMP280']
                temp = bmp['Temperature']
                press = bmp['Pressure']
                sensor_address = '{}_{}'.format(sensor_name, 'bmp280')
                current_zone_sensor = models.ZoneSensor.query.filter_by(sensor_address=sensor_address).first()
                if current_zone_sensor is not None:
                    current_sensor = models.Sensor.query.filter_by(address=sensor_address).first()
                    sensor = models.Sensor(address=sensor_address, sensor_name=current_zone_sensor.sensor_name)
                    sensor.temperature = temp
                    sensor.pressure = press
                    sensor.save_changed_fields(
                        current_record=current_sensor, notify_transport_enabled=True, save_to_graph=True)
                else:
                    L.l.info('Undefined sensor found in {}, value={}'.format(sensor_address, bmp))
            else:
                L.l.warning("Usefull payload missing from topic {} payload={}".format(msg.topic, msg.payload))
        else:
            L.l.warning("Invalid sensor topic {}".format(msg.topic))
Esempio n. 8
0
def __update_ddns_rackspace():
    try:
        ConfigFile = get_json_param(Constant.P_DDNS_RACKSPACE_CONFIG_FILE)
        with open(ConfigFile, 'r') as f:
            config_list = json.load(f)
        global cache
        if cache == {} or cache is None:
            cache = {}
            cache['auth'] = {}
            cache['auth']['expires'] = str(utils.get_base_location_now_date())
        config = {}
        ip_json_test = ''
        try:
            #ip_json_test = requests.get('http://ip-api.com/json').text
            ip_json_test = requests.get('http://ipinfo.io').text
            ip_json_obj = utils.json2obj(ip_json_test)
            #public_ip = ip_json_obj['query']
            #public_isp = ip_json_obj['isp']
            public_ip = ip_json_obj['ip']
            public_isp = ip_json_obj['org']
        except Exception as ex:
            L.l.warning('Unable to get my ip, err={} text={}'.format(
                ex, ip_json_test))
            return

        for config in config_list.values():
            # check if public address is for this config dns entry
            isp = config['isp']
            if isp != public_isp:
                continue

            # get IP address
            try:
                cache['ip:' + isp] = socket.gethostbyname(
                    config['record_name'])
            except Exception as ex:
                cache['ip:' + isp] = None
                L.l.warning('Unable to get ip for host {}, err={}'.format(
                    config['record_name'], ex))

            if public_ip == '' or public_ip is None or public_ip == cache[
                    'ip:' + isp]:
                L.l.debug('IP address for ' + isp + ' is still ' + public_ip +
                          '; nothing to update.')
                return
            else:
                L.l.info('IP address was changed for {}, old was {} new is {}'.
                         format(isp, cache['ip:' + isp], public_ip))

            cache['ip:' + isp] = public_ip
            now = utils.get_base_location_now_date()
            expires = parser.parse(cache['auth']['expires'])
            now = pytz.utc.localize(now)
            expires = pytz.utc.localize(expires)
            if expires <= now:
                L.l.info(
                    'Expired rackspace authentication token; reauthenticating...'
                )
                # authenticate with Rackspace
                authUrl = 'https://identity.api.rackspacecloud.com/v2.0/tokens'
                authData = {
                    'auth': {
                        'RAX-KSKEY:apiKeyCredentials': {
                            'username': config['username'],
                            'apiKey': config['api_key']
                        }
                    }
                }
                authHeaders = {
                    'Accept': 'application/json',
                    'Content-type': 'application/json'
                }
                auth = requests.post(authUrl,
                                     data=json.dumps(authData),
                                     headers=authHeaders)
                auth = utils.json2obj(auth.text)
                cache['auth']['expires'] = auth['access']['token']['expires']
                cache['auth']['token'] = auth['access']['token']['id']

            # update DNS record
            url = 'https://dns.api.rackspacecloud.com/v1.0/' + config['account_id'] + \
                  '/domains/' + config['domain_id'] + '/records/' + config['record_id']
            data = {
                'ttl': config['record_ttl'],
                'name': config['record_name'],
                'data': public_ip
            }
            headers = {
                'Accept': 'application/json',
                'Content-type': 'application/json',
                'X-Auth-Token': cache['auth']['token']
            }
            result = requests.put(url, data=json.dumps(data), headers=headers)
            if result.ok:
                L.l.info('Updated IP address for {} to {}'.format(
                    config['record_name'], public_ip))
            else:
                L.l.warning('Unable to update IP, response={}'.format(result))
    except Exception as ex:
        L.l.warning('Unable to check and update dns, err={}'.format(ex))
Esempio n. 9
0
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