Пример #1
0
    def setup_timers(self):
        logger.info("setting up cron")
        callbacks = {}
        for prop in self.properties:
            interval = self.properties[prop]['interval']
            if interval not in callbacks:
                callbacks[interval] = CallBack(self)
            callbacks[interval].add_property(prop)

        for cb in callbacks:
            repeating_timer = Repeating_Timer(cb)
            repeating_timer.add_callback(callbacks[cb])
Пример #2
0
    def start(
        self
    ):  # called after the device has been built with nodes and properties
        logger.debug('Device startup')
        self.start_time = time.time()

        global repeating_timer
        if repeating_timer == None:
            repeating_timer = Repeating_Timer(
                self.homie_settings['update_interval'])

        repeating_timer.add_callback(self.publish_statistics)

        if self.mqtt_client.mqtt_connected:  #run start up tasks if mqtt is ready, else wait for on_connect message from mqtt client
            self.mqtt_on_connection(True)
Пример #3
0
    def __init__(
                self, 
                device_id=None, 
                name=None, 
                homie_settings=None, 
                mqtt_settings=None,
                device_comfoairq=None,
                connect_at_start=True,

    ):
        assert device_id
        assert name
        assert device_comfoairq
        assert mqtt_settings
        super().__init__ (device_id, name, homie_settings, mqtt_settings)

        self.device_comfoairq=device_comfoairq
# Gateway Controls
        node = Node_Base(self,'controls','ComfoAirQ Gateway Controls','controls')
        self.add_node (node)

        node.add_property(Property_Switch(node,id='stayconnected',name = 'Stay connected to Comfoconnect' ,settable = True, set_value=self.set_stay_connected))

        node.add_property(Property_Switch(node,id='reload',name = 'Publish Homie Devices' ,settable = True, set_value=self.set_reload))

        node = Node_Base(self,'sensors','ComfoAirQ Gateway Sensors','sensors')
        self.add_node (node)



        node.add_property (Property_Enum (node,id='state',name = 'Connection state',settable = False,data_format=','.join(DEVICE_STATES)))

        if self.device_comfoairq is not None:
            self.device_comfoairq.comfoairq.add_on_state_change_callback(self.publish_connection_status)
        
        global repeating_timer
        if repeating_timer == None:
            repeating_timer = Repeating_Timer(
                self.homie_settings["update_interval"]
            )

        repeating_timer.add_callback(self.publish_connection_status)

        self.start()
        if connect_at_start:
            self.set_stay_connected("ON")

        self.publish_connection_status()
Пример #4
0
    def start(self):  # called after the device has been built with nodes and properties
        logger.info("Device Start {}".format(self.name))
        self.start_time = time.time()

        if "stats" in self.extensions:
            global repeating_timer
            if repeating_timer == None:
                repeating_timer = Repeating_Timer(
                    self.homie_settings["update_interval"]
                )

            repeating_timer.add_callback(self.publish_uptime)

        if (
            self.mqtt_client.mqtt_connected
        ):  # run start up tasks if mqtt is ready, else wait for on_connect message from mqtt client
            self.mqtt_on_connection(True)
Пример #5
0
 def start_publishing_loop(self):
     # assign timer to publish info
     self._timer_upd_interval = Repeating_Timer(self._upd_interval)
     self._timer_upd_interval.add_callback(self._publish_data)
Пример #6
0
class Device_BME680(Device_Base):
    
    _MAX_RESISTANCE = 350000
    _MIN_RESISTANCE = 10000
    _HUMIDITY_WEIGHT = 0.25
    _upd_interval = 60
    _ideal_rel_humidity = 40.
    _sensor_settings = {
            "temperature_bias": 0.,
            "humidity_bias": 0.,
            "pressure_bias": 0.
            }
    


    def __init__(self, device_id=None, name=None, 
            sensor_settings={}, homie_settings=None, mqtt_settings=None):
        super().__init__(device_id, name, homie_settings, mqtt_settings)
        
        # assign sensor
        try:
            sensor = bme680.BME680(bme680.I2C_ADDR_PRIMARY)
        except IOError:
            sensor = bme680.BME680(bme680.I2C_ADDR_SECONDARY)
        #sensor.set_humidity_oversample(bme680.OS_2X)
        sensor.set_humidity_oversample(bme680.OS_8X)
        #sensor.set_pressure_oversample(bme680.OS_4X)
        #sensor.set_temperature_oversample(bme680.OS_8X)
        #sensor.set_filter(bme680.FILTER_SIZE_3)
        #sensor.set_gas_status(bme680.ENABLE_GAS_MEAS)
        sensor.set_gas_heater_temperature(320)
        sensor.set_gas_heater_duration(150)
        sensor.select_gas_heater_profile(0)
        self._sensor = sensor
        self._sensor_settings.update(sensor_settings)


        # assign Homie features to update/request information on demand
        node_worker = Node_Base(self, id="worker-node", name="Data worker node", type_="status")
        self.add_node(node_worker)
 
        upd_interval = Property_Integer(node=node_worker, 
                id="update-interval", name="Statistics update interval", 
                unit="s", settable=True, set_value=self._set_upd_interval, 
                value=self._upd_interval, data_format="5:3600")
        node_worker.add_property(upd_interval)
   
        ideal_rel_humidity = Property_Integer(node=node_worker,
                id="update-ideal-rel-humidity", name="Update ideal relative humidity for AQI calculations",
                unit="%", settable=True, set_value=self._set_ideal_rel_humidity,
                value=self._ideal_rel_humidity, data_format="0:100")
        node_worker.add_property(ideal_rel_humidity)


        # assign Homie features to base sensor
        node_sensor = Node_Base(self, id="multi-sensor", name="Multiple sensor", type_="status")
        self.add_node(node_sensor)

        pressure_prop = Property_Pressure(node=node_sensor, unit="hPa")
        node_sensor.add_property(pressure_prop)
        pressure_bias_prop = Property_Pressure(node=node_sensor, id="pressure-bias", unit="hPa", settable=False, value=self._sensor_settings["pressure_bias"])
        node_sensor.add_property(pressure_bias_prop)
        self._pressure = pressure_prop
        humidity_prop = Property_Humidity(node=node_sensor)
        node_sensor.add_property(humidity_prop)
        humidity_bias_prop = Property_Humidity(node=node_sensor, id="humidity-bias", settable=False, value=self._sensor_settings["humidity_bias"])
        node_sensor.add_property(humidity_bias_prop)
        self._humidity = humidity_prop
        temperature_prop = Property_Temperature(node=node_sensor, unit="°C")
        node_sensor.add_property(temperature_prop)
        temperature_bias_prop = Property_Temperature(node=node_sensor, id="temperature-bias", unit="°C", settable=False, value=self._sensor_settings["temperature_bias"])
        node_sensor.add_property(temperature_bias_prop)
        self._temperature = temperature_prop
        gas_res_prop = Property_Float(node=node_sensor, id="gas-resistance", name="Gas resistance", settable=False, unit="Ohm")
        node_sensor.add_property(gas_res_prop)
        self._gas_resistance = gas_res_prop
        aqi_prop = Property_Integer(node=node_sensor, id="aqi", name="Air quality index", settable=False)
        node_sensor.add_property(aqi_prop)
        self._aqi = aqi_prop


        # start 
        self.start()
    
    def _get_core_data(self):
        self._sensor.get_sensor_data()
        if self._mqtt_connected:
            self._pressure.value = self._sensor.data.pressure + self._sensor_settings["pressure_bias"]
            self._temperature.value = self._sensor.data.temperature + self._sensor_settings["temperature_bias"]
            self._humidity.value = self._sensor.data.humidity + self._sensor_settings["humidity_bias"]
            if self._sensor.data.heat_stable:
                adj_resistance = self._adj_gas_resistance(self._sensor.data.gas_resistance, self._sensor.data.humidity)
                self._gas_resistance.value = adj_resistance
                self._aqi.value = self._calculate_aqi(adj_resistance, self._sensor.data.humidity)
        else:
            pass

    def _adj_gas_resistance(self, gas_resistance, humidity):
        # based on observations, gas resistance is dependent on humidity more than temperature
        # so i adjust for it and changed the parameter
        # https://forums.pimoroni.com/t/bme680-observed-gas-ohms-readings/6608/16
        return math.exp(math.log(gas_resistance) + 0.023 * humidity)


    def _calculate_aqi(self, gas_resistance, humidity):
        if humidity < self._ideal_rel_humidity:
            #if humidity == ideal_rel_humidity, then result is 0.
            #if humidity approaches 0, result is HUMIDITY WEIGHT
            hum_score = self._HUMIDITY_WEIGHT * (self._ideal_rel_humidity - humidity) / self._ideal_rel_humidity
        else:
            #if humidity == ideal_rel_humidity, then result is 0.
            #if humidity approaches 100, result is HUMIDITY_WEIGHT
            hum_score = self._HUMIDITY_WEIGHT * (humidity - self._ideal_rel_humidity) / (100. - self._ideal_rel_humidity)
        
        if gas_resistance > self._MAX_RESISTANCE:
            gas_resistance = self._MAX_RESISTANCE - 1.0 #to avoid numerical errors below in ratios
        elif gas_resistance < self._MIN_RESISTANCE:
            gas_resistance = self._MIN_RESISTANCE + 1.0
        gas_score = (1. - self._HUMIDITY_WEIGHT) * (1. - (gas_resistance - self._MIN_RESISTANCE)/(self._MAX_RESISTANCE - self._MIN_RESISTANCE))
        return int(500 * (gas_score + hum_score))

    def _publish_data(self):
        if self.mqtt_client.mqtt_connected:
            self._get_core_data()

    @property
    def ideal_rel_humidity(self):
        return self._ideal_rel_humidity
    #needed for set_value routine in node Property
    def _set_ideal_rel_humidity(self):
        self.ideal_rel_humidity = value
    @ideal_rel_humidity.setter
    def ideal_rel_humidity(self,value):
        self._ideal_rel_humidity = value

    @property
    def upd_interval(self):
        return self._upd_interval
    #needed for set_value routine in node Property
    def _set_upd_interval(self, value):
        self.upd_interval = value
    @upd_interval.setter
    def upd_interval(self, value):
        self._upd_interval = value
        self._timer_upd_interval.interval = value

    def start_publishing_loop(self):
        # assign timer to publish info
        self._timer_upd_interval = Repeating_Timer(self._upd_interval)
        self._timer_upd_interval.add_callback(self._publish_data)

    def stop_publishing_loop(self):
        try:
            if self._timer_upd_interval.timer.is_alive():
                self._timer_upd_interval.stop()
        except AttributeError:
            pass
    def __init__(
                self, 
                device_id=None, 
                name=None, 
                homie_settings=None, 
                mqtt_settings=None,
                sensors_definition = comfoairq_sensors,
                comfoconnect_settings=None,                
    ):
        assert comfoconnect_settings
        assert mqtt_settings

        super().__init__ (device_id, name, homie_settings, mqtt_settings)

        self.state = 'init'
        global repeating_timer
        if repeating_timer == None:
            repeating_timer = Repeating_Timer(
                self.homie_settings["update_interval"]
            )

        repeating_timer.add_callback(self.publish_connection_status)

        self.sensors = sensors_definition
        self.comfoconnect_settings=comfoconnect_settings
        self.min_low_flow = self.comfoconnect_settings['COMFOCONNECT_MIN_LOW_FLOW']

        self.max_high_flow = self.comfoconnect_settings['COMFOCONNECT_MAX_HIGH_FLOW']

        self.comfoairq = ComfoAirQ(comfoconnect_settings=self.comfoconnect_settings)
        self.comfoairq.callback_sensor = self.callback_sensor

        self.comfoairq.add_on_state_change_callback(self.publish_connection_status)

# Sensors

        node = Node_Base(self,'sensors','Sensors','sensors')
        self.add_node (node)

        for comfoairq_sensor in self.sensors:
            self.comfoairq.register_sensor(comfoairq_sensor)
            for homie_sensor in self.sensors[comfoairq_sensor]:
                sensor_id ,sensor_name ,sensor_type, transformation_function, function_args =  homie_sensor
                if sensor_type == 'temperature':
                    node.add_property (Property_Temperature (node,id=sensor_id,name = sensor_name, unit='°C'))
                elif sensor_type == 'fan_speed':
                    node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='rpm'))
                elif sensor_type == 'fan_flow':
                    node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='m³/h'))
                elif sensor_type == 'percentage':
                    node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='%'))
                elif sensor_type == 'mode_end_date':
                    node.add_property (Property_DateTime (node,id=sensor_id,name = sensor_name,settable = False,data_format='%Y-%m-%d %H:%M:%S.%f'))
                elif sensor_type == 'mode_timer':
                    node.add_property (Property_Integer (node,id=sensor_id,name = sensor_name,settable = False,unit='s'))
                elif sensor_type == 'enum':
                    node.add_property (Property_Enum (node,id=sensor_id,name = sensor_name,settable = False,data_format=','.join(function_args.values())))
                elif sensor_type == 'power_current':
                    node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='W'))
                elif sensor_type == 'energy':
                    node.add_property (Property_Float (node,id=sensor_id,name = sensor_name,settable = False,unit='kWh'))
                elif sensor_type == 'integer':
                    node.add_property (Property_Integer(node,id=sensor_id,name = sensor_name,settable = False))

# Sensors managed same way as Controls 

        node.add_property(Property_Enum (node,id='current-mode',name ='Current Mode',settable = False,data_format=','.join(CURRENT_OPERATING_MODE_SENSOR_VALUES.values())))
        self.add_controls_callback(SENSOR_OPERATING_MODE_BIS,self.update_current_mode)
        self.add_controls_callback(SENSOR_OPERATING_MODE,self.update_current_mode)

# Controls

        node = Node_Base(self,'controls','Controls','controls')
        self.add_node (node)

# FAN SPEED MODE
        node.add_property(Property_Enum (node,id='fan-mode',name='Fan Mode',data_format=','.join(FAN_MODES.keys()),set_value = lambda value: self.set_fan_mode(value)))
        self.add_controls_callback(SENSOR_FAN_SPEED_MODE,self.update_fan_mode)

# BYPASS  MODE (for 1 hour)
        node.add_property(Property_Enum (node,id='bypass-mode',name='Bypass Mode',data_format=','.join(BYPASS_MODES.keys()),set_value = lambda value: self.set_bypass_mode(value)))
        self.add_controls_callback(SENSOR_BYPASS_MODE,self.update_bypass_mode)

# BYPASS ON 
        node.add_property(Property_Integer (node,id='bypass-on', name='Bypass On',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_bypass_on(value)))
# BYPASS OFF
        node.add_property(Property_Integer (node,id='bypass-off', name='Bypass Off',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_bypass_off(value)))

        self.add_controls_callback(SENSOR_BYPASS_MODE,self.update_bypass)
        self.add_controls_callback(SENSOR_BYPASS_TIMER,self.update_bypass)

# TEMPERATURE PROFILE
        node.add_property(Property_Enum (node,id='temperature-profile',name='Temperature Profile',data_format=','.join(TEMPERATURE_PROFILES.keys()),set_value = lambda value: self.set_temperature_profile(value)))
        self.add_controls_callback(SENSOR_TEMPERATURE_PROFILE,self.update_temperature_profile)

# OPERATING MODE
        node.add_property(Property_Enum (node,id='operating-mode',name='Operating Mode',data_format=','.join(OPERATING_MODES.keys()),set_value = lambda value: self.set_operating_mode(value)))
        self.add_controls_callback(SENSOR_OPERATING_MODE,self.update_operating_mode)

# Manual MODE
        node.add_property(Property_Switch (node,id='manual-mode',name='Manual Mode',set_value = lambda value: self.set_manual_mode(value)))
        self.add_controls_callback(SENSOR_OPERATING_MODE,self.update_manual_mode)

# VENT MODE
        node.add_property(Property_Enum (node,id='vent-mode',name='Ventilation Mode',data_format=','.join(VENT_MODES.keys()),set_value = lambda value: self.set_vent_mode(value)))
        self.add_controls_callback(SENSOR_TEMPORARY_STOP_EXHAUST_FAN_STATE,self.update_vent_mode)
        self.add_controls_callback(SENSOR_TEMPORARY_STOP_SUPPLY_FAN_STATE,self.update_vent_mode)

# SUPPLY FAN OFF 
        node.add_property(Property_Integer (node,id='supply-fan-off', name='Supply Fan Off',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_supply_fan_off(value)))
        self.add_controls_callback(SENSOR_SUPPLY_FAN_TIMER,self.update_supply_fan_off)

# EXHAUST FAN  OFF
        node.add_property(Property_Integer (node,id='exhaust-fan-off', name='Exhaust Fan Off',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_exhaust_fan_off(value)))
        self.add_controls_callback(SENSOR_EXHAUST_FAN_TIMER,self.update_exhaust_fan_off)

# BOOST MODE
        node.add_property(Property_Integer (node,id='boost-mode',name='Activate Scheduled Boost Mode',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_boost_mode(value)))
        self.add_controls_callback(SENSOR_OPERATING_MODE_BIS,self.update_boost_mode)
        self.add_controls_callback(SENSOR_FAN_NEXT_CHANGE,self.update_boost_mode)

# AWAY MODE
        node.add_property(Property_Integer (node,id='away-mode',name='Activate Scheduled Away Mode',data_format='0:'+ str(0xffffffff),set_value = lambda value: self.set_away_mode(value)))
        self.add_controls_callback(SENSOR_OPERATING_MODE_BIS,self.update_away_mode)
        self.add_controls_callback(SENSOR_FAN_NEXT_CHANGE,self.update_away_mode)

# Heating Season detection RMOT
        node.add_property(Property_Temperature (node,id='heating-rmot',name='Heating Limit RMOT',data_format='0:20',unit='°C',settable = True, set_value = lambda value: self.set_heating_rmot(value)))
        self.comfoairq.add_on_state_change_callback(self.update_heating_rmot)
        repeating_timer.add_callback(self.update_heating_rmot)

# Cooling Season detection RMOT
        node.add_property(Property_Temperature (node,id='cooling-rmot',name='Cooling Limit RMOT',data_format='20:40',unit='°C',settable = True, set_value = lambda value: self.set_cooling_rmot(value)))
        self.comfoairq.add_on_state_change_callback(self.update_cooling_rmot)
        repeating_timer.add_callback(self.update_cooling_rmot)

#Temperature  passive
        node.add_property(Property_Enum (node,id='temperature-passive',name='Temperature Passive',data_format=','.join(SENSOR_VENTILATION),set_value = lambda value: self.set_temperature_passive(value)))
        self.comfoairq.add_on_state_change_callback(self.update_temperature_passive)
        repeating_timer.add_callback(self.update_temperature_passive)

#Humidity comfort
        node.add_property(Property_Enum (node,id='humidity-comfort',name='Humidity Comfort',data_format=','.join(SENSOR_VENTILATION),set_value = lambda value: self.set_humidity_comfort(value)))
        self.comfoairq.add_on_state_change_callback(self.update_humidity_comfort)
        repeating_timer.add_callback(self.update_humidity_comfort)

#Humidity protection
        node.add_property(Property_Enum (node,id='humidity-protection',name='Humidity Protection',data_format=','.join(SENSOR_VENTILATION),set_value = lambda value: self.set_humidity_protection(value)))
        self.comfoairq.add_on_state_change_callback(self.update_humidity_protection)
        repeating_timer.add_callback(self.update_humidity_protection)

#Flow unbalance
        node.add_property(Property_Float (node,id='unbalance',name='Flow Unbalance',data_format='-15:15',set_value = lambda value: self.set_unbalance(value)))
        self.comfoairq.add_on_state_change_callback(self.update_unbalance)
        repeating_timer.add_callback(self.update_unbalance)

#Flow Away
        node.add_property(Property_Integer (node,id='flow-away',name='Flow Away Speed',data_format='0:' + str(self.max_high_flow),set_value = lambda value: self.set_flow_away(value)))
        self.comfoairq.add_on_state_change_callback(self.update_flow_away)
        repeating_timer.add_callback(self.update_flow_away)

#Flow Low
        node.add_property(Property_Integer (node,id='flow-low',name='Flow Low Speed',data_format= str(self.min_low_flow) + ':' + str(self.max_high_flow),set_value = lambda value: self.set_flow_low(value)))
        self.comfoairq.add_on_state_change_callback(self.update_flow_low)
        repeating_timer.add_callback(self.update_flow_low)

#Flow Medium
        node.add_property(Property_Integer (node,id='flow-medium',name='Flow Medium Speed',data_format=str(self.min_low_flow) + ':' + str(self.max_high_flow),set_value = lambda value: self.set_flow_medium(value)))
        self.comfoairq.add_on_state_change_callback(self.update_flow_medium)
        repeating_timer.add_callback(self.update_flow_medium)

#Flow High
        node.add_property(Property_Integer (node,id='flow-high',name='Flow High Speed',data_format=str(self.min_low_flow) + ':' + str(self.max_high_flow),set_value = lambda value: self.set_flow_high(value)))
        self.comfoairq.add_on_state_change_callback(self.update_flow_high)
        repeating_timer.add_callback(self.update_flow_high)

        self.start()
        self.publish_connection_status()