class OnAtStartup(ActorBase): actor_setting = Property.Actor( "Actor", description="Select the actor you would like to have ON at startup") power_setting = Property.Number( "Power", True, 100, description="Select the power of the actor at startup") def init(self): if not 0 <= int(self.power_setting) <= 100: self.api.notify(headline="OnAtStartup Error", message="Power must be between 0 and 100", timeout=None, type="danger") raise ValueError("Power must be between 0 and 100") else: self.api.switch_actor_on(int(self.actor_setting), power=int(self.power_setting)) def set_power(self, power): pass def off(self): pass def on(self, power=None): pass
class ActorGroup(ActorBase): actordesc = "Select an actor to be controlled by this group." actor01 = Property.Actor("Actor 1", description=actordesc) actor02 = Property.Actor("Actor 2", description=actordesc) actor03 = Property.Actor("Actor 3", description=actordesc) actor04 = Property.Actor("Actor 4", description=actordesc) actor05 = Property.Actor("Actor 5", description=actordesc) actor06 = Property.Actor("Actor 6", description=actordesc) actor07 = Property.Actor("Actor 7", description=actordesc) actor08 = Property.Actor("Actor 8", description=actordesc) def init(self): self.actors = [] if isinstance(self.actor01, unicode) and self.actor01: self.actors.append(int(self.actor01)) if isinstance(self.actor02, unicode) and self.actor02: self.actors.append(int(self.actor02)) if isinstance(self.actor03, unicode) and self.actor03: self.actors.append(int(self.actor03)) if isinstance(self.actor04, unicode) and self.actor04: self.actors.append(int(self.actor04)) if isinstance(self.actor05, unicode) and self.actor05: self.actors.append(int(self.actor05)) if isinstance(self.actor06, unicode) and self.actor06: self.actors.append(int(self.actor06)) if isinstance(self.actor07, unicode) and self.actor07: self.actors.append(int(self.actor07)) if isinstance(self.actor08, unicode) and self.actor08: self.actors.append(int(self.actor08)) def set_power(self, power): for actor in self.actors: self.api.actor_power(actor, power=power) def on(self, power=None): for actor in self.actors: self.api.switch_actor_on(actor, power=power) def off(self): turnedOnActors = [] for idx, value in cbpi.cache["actors"].iteritems(): if (value.type == "ActorGroup" and idx != self.id and value.state == 1): actorsIds = map(int, value.config.values()) intersection = intersect(actorsIds, self.actors) turnedOnActors.extend(intersection) for actor in self.actors: if (actor not in turnedOnActors): self.api.switch_actor_off(actor)
class ScalableDependentActor(ActorBase): base = Property.Actor(label="Dependent Actor", description="Select the actor which will be dependent to master one.") dependency = Property.Actor(label="Master Actor", description="Select the actor that will control the dependent one selected above.") def init(self): # Switch off the Dependent Actor self.api.switch_actor_off(int(self.base)) # Hide Base Actor for idx, value in cbpi.cache["actors"].iteritems(): if idx == int(self.base): value.hide = True # Synchronize current power setting with the Base Actor def set_power(self, power): potenciaMaster = self.api.actor_power(int(self.dependency)) potenciaDif = 100 - int(potenciaMaster) self.api.actor_power(int(self.base), power=int(potenciaDif)) def off(self): self.api.switch_actor_off(int(self.base)) def on(self, power=None): dependency_name = "" for idx, value in cbpi.cache["actors"].iteritems(): if idx == int(self.dependency): dependency_name = value.name for idx, value in cbpi.cache["actors"].iteritems(): if idx == int(self.dependency): if (value.state == 0): self.api.switch_actor_on(int(self.base), power=100) elif (value.state == 1) & (self.api.actor_power(int(self.dependency))==100): self.api.switch_actor_off(int(self.base)) elif (value.state == 1) & (self.api.actor_power(int(self.dependency))<100): potenciaMaster = self.api.actor_power(int(self.dependency)) potenciaDif = 100 - int(potenciaMaster) self.api.switch_actor_on(int(self.base), power=potenciaDif)
class ActorGroup(ActorBase): actordesc = "Select an actor to be controlled by this group." actor01 = Property.Actor("Actor 1", description=actordesc) actor02 = Property.Actor("Actor 2", description=actordesc) actor03 = Property.Actor("Actor 3", description=actordesc) actor04 = Property.Actor("Actor 4", description=actordesc) actor05 = Property.Actor("Actor 5", description=actordesc) actor06 = Property.Actor("Actor 6", description=actordesc) actor07 = Property.Actor("Actor 7", description=actordesc) actor08 = Property.Actor("Actor 8", description=actordesc) def init(self): self.actors = [] if isinstance(self.actor01, unicode) and self.actor01: self.actors.append(int(self.actor01)) if isinstance(self.actor02, unicode) and self.actor02: self.actors.append(int(self.actor02)) if isinstance(self.actor03, unicode) and self.actor03: self.actors.append(int(self.actor03)) if isinstance(self.actor04, unicode) and self.actor04: self.actors.append(int(self.actor04)) if isinstance(self.actor05, unicode) and self.actor05: self.actors.append(int(self.actor05)) if isinstance(self.actor06, unicode) and self.actor06: self.actors.append(int(self.actor06)) if isinstance(self.actor07, unicode) and self.actor07: self.actors.append(int(self.actor07)) if isinstance(self.actor08, unicode) and self.actor08: self.actors.append(int(self.actor08)) def set_power(self, power): for actor in self.actors: self.api.actor_power(actor, power=power) def on(self, power=None): for actor in self.actors: self.api.switch_actor_on(actor, power=power) def off(self): for actor in self.actors: self.api.switch_actor_off(actor)
class MCP4728Actor(ActorBase): """An actor that uses a four channel MCP4728 12-bit DAC i2c module to control power using output voltages between 0-Vdd or 0-Vref x Gain on a single channel. The on/off state of the actor can be controlled either by setting the DAC output to zero, or using an additional actor. """ a_address = Property.Select("DAC Address", [0, 1, 2, 3, 4, 5, 6, 7], description="Minor address of the MCP4728 DAC unit. Use 0 unless you have changed the address manually") b_channel = Property.Select("Channel", [0, 1, 2, 3], description="DAC channel to use") c_volt_ref = Property.Select("Reference Voltage", ["Vdd", "Internal 2.048V", "Internal 4.096V"], description="Voltage Reference for DAC channel") d_power_ctrl = Property.Select("Power Control", ["Zero DAC", "Actor"], description="Power control method") e_power_actor = Property.Actor("Power On/Off Actor", description="Actor to use to control power") timeout = Property.Number("Notification duration (ms)", True, 1000, description="0ms will disable notifications completely") z_debug = Property.Select("Debug Messages", ["Off", "On"], description="Display debug notifications") def init(self): address = int(self.a_address) channel = int(self.b_channel) self.dac = mcp4728.MCP4728(address) if self.z_debug == "On": cbpi.notify("Connected to MCP4728", "DAC Address {:d}: DAC Channel {:d}".format(address, channel), timeout=self.timeout) if self.c_volt_ref == "Vdd": self.dac.set_vref(channel, 0) else: self.dac.set_vref(channel, 1) if self.c_volt_ref == "Internal 4.096V": self.dac.set_gain(channel, 1) else: self.dac.set_gain(channel, 0) # CBPI Actor sets UI state to 0 and power to 100 in post_init_callback, called after this method, # so set power here to 100 and state to off to make sure we start in a consistent state. # # Have to predefine .state and .power, because they aren't defined by ActorAPI.init self.api.cache.get("actors").get(self.id).state = 0 self.api.cache.get("actors").get(self.id).power = 100 self.api.switch_actor_off(self.id) self.api.actor_power(self.id, 100) self.value = 4095 def set_power(self, power): """Set the power as a percentage of the range between minimum and maximum power""" channel = int(self.b_channel) self.power = power self.value = int((4095 * power) / 100) if self.d_power_ctrl == "Zero DAC": if self.state == 0: pass elif self.state == 1: self.dac.set_value(channel, self.value) else: self.dac.set_value(channel, self.value) if self.z_debug == "On": value = self.dac.get_value(channel) cbpi.notify("MCP4728 Set Value", "Channel {:d}: Value {:d}".format(channel, value), timeout=self.timeout) def off(self): """Switch the actor off""" channel = int(self.b_channel) if self.d_power_ctrl == "Actor": self.api.switch_actor_off(int(self.e_power_actor)) else: self.dac.set_value(channel, 0) self.state = 0 if self.z_debug == "On": value = self.dac.get_value(channel) cbpi.notify("MCP4728 Current Value", "Channel {:d}: Value {:d}".format(channel, value), timeout=self.timeout) def on(self, power=None): """Switch the actor on. Set the power to the given value or the current power setting.""" channel = int(self.b_channel) if self.d_power_ctrl == "Actor": self.api.switch_actor_on(int(self.e_power_actor)) self.state = 1 if power: self.set_power(power) else: self.dac.set_value(channel, self.value) if self.z_debug == "On": value = self.dac.get_value(channel) cbpi.notify("MCP4728 Current Value", "Channel {:d}: Value {:d}".format(channel, value), timeout=self.timeout)
class DefinedState(ActorBase): #custom property actordesc = "select an actor to control state" actor01 = Property.Actor("Actor 1", description=actordesc) actor01_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor02 = Property.Actor("Actor 2", description=actordesc) actor02_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor03 = Property.Actor("Actor 3", description=actordesc) actor03_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor04 = Property.Actor("Actor 4", description=actordesc) actor04_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor05 = Property.Actor("Actor 5", description=actordesc) actor05_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor06 = Property.Actor("Actor 6", description=actordesc) actor06_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor07 = Property.Actor("Actor 7", description=actordesc) actor07_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") actor08 = Property.Actor("Actor 8", description=actordesc) actor08_On_Off = Property.Select("On or OFF", options=["On", "Off"], description="On or Off") #ta_stop = Property.Number("Pause Timer", configurable=True, description="Defines how long the Agitator should stop before run again.") def init(self): self.actors = [] self.YesNo = [] if isinstance(self.actor01, unicode) and self.actor01: self.actors.append(int(self.actor01)) self.YesNo.append(self.actor01_On_Off) if isinstance(self.actor02, unicode) and self.actor02: self.actors.append(int(self.actor02)) self.YesNo.append(self.actor02_On_Off) if isinstance(self.actor03, unicode) and self.actor03: self.actors.append(int(self.actor03)) self.YesNo.append(self.actor03_On_Off) if isinstance(self.actor04, unicode) and self.actor04: self.actors.append(int(self.actor04)) self.YesNo.append(self.actor04_On_Off) if isinstance(self.actor05, unicode) and self.actor05: self.actors.append(int(self.actor05)) self.YesNo.append(self.actor05_On_Off) if isinstance(self.actor06, unicode) and self.actor06: self.actors.append(int(self.actor06)) self.YesNo.append(self.actor06_On_Off) if isinstance(self.actor07, unicode) and self.actor07: self.actors.append(int(self.actor07)) self.YesNo.append(self.actor07_On_Off) if isinstance(self.actor08, unicode) and self.actor08: self.actors.append(int(self.actor08)) self.YesNo.append(self.actor08_On_Off) def set_power(self, power): for actor in self.actors: self.api.actor_power(actor, power=power) #self.sleep(int(self.ta_stop)) def on(self, power=None): i = 0 for actor in self.actors: if self.YesNo[i] == "On": self.api.switch_actor_on(actor, power=power) else: self.api.switch_actor_off(actor) i = i + 1 #self.sleep(int(self.ta_stop)) #self.api.cache.get("actors").get(int(self.id)).timer = int(time.time()) + int(self.ta_stop) -1 #self.api.notify(headline="Timed Agitator", message="Timed Agitator started", type="info") def off(self): for actor in self.actors: self.api.switch_actor_off(actor)
class SlaveActorControl(ActorBase): actordesc = "Select an actor to be controlled by this group." a_actor= Property.Actor("Slave Actor", description= "Actor to be driven if any others on") actor01 = Property.Actor("Actor 1", description=actordesc) actor02 = Property.Actor("Actor 2", description=actordesc) actor03 = Property.Actor("Actor 3", description=actordesc) actor04 = Property.Actor("Actor 4", description=actordesc) actor05 = Property.Actor("Actor 5", description=actordesc) actor06 = Property.Actor("Actor 6", description=actordesc) actor07 = Property.Actor("Actor 7", description=actordesc) actor08 = Property.Actor("Actor 8", description=actordesc) def init(self): self.actors = [] self.manual_on = False if isinstance(self.a_actor, unicode) and self.a_actor: self.slave_actor = (int(self.a_actor)) else: self.slave_actor = None if isinstance(self.actor01, unicode) and self.actor01: self.actors.append(int(self.actor01)) if isinstance(self.actor02, unicode) and self.actor02: self.actors.append(int(self.actor02)) if isinstance(self.actor03, unicode) and self.actor03: self.actors.append(int(self.actor03)) if isinstance(self.actor04, unicode) and self.actor04: self.actors.append(int(self.actor04)) if isinstance(self.actor05, unicode) and self.actor05: self.actors.append(int(self.actor05)) if isinstance(self.actor06, unicode) and self.actor06: self.actors.append(int(self.actor06)) if isinstance(self.actor07, unicode) and self.actor07: self.actors.append(int(self.actor07)) if isinstance(self.actor08, unicode) and self.actor08: self.actors.append(int(self.actor08)) if not int(self.id) in master_actor_ids: master_actor_ids.append(int(self.id)) def execute_func(self): active = False if self.manual_on == True: active = True for actor in self.actors: if cbpi.cache.get("actors").get(actor).state == True: active = True if (self.slave_actor is not None) and (cbpi.cache.get("actors").get(self.slave_actor).state != active): if active == True: self.api.switch_actor_on(self.slave_actor) else: self.api.switch_actor_off(self.slave_actor) def set_power(self, power): if self.slave_actor is not None: self.api.actor_power(self.slave_actor, power=power) def on(self, power=None): print "Slave on" self.manual_on = True if self.slave_actor is not None: self.api.switch_actor_on(self.slave_actor) def off(self): print "Slave off" self.manual_on = False
class GPIODependentActor(ActorBase): base = Property.Actor( label="Base Actor", description="Select the actor you would like to add a dependency to.") dependent_gpio = Property.Select("Dependent GPIO", options=[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27 ]) dependency_type = Property.Select( label="Dependency Type", options=["HIGH", "LOW"], description= "Select the dependency type. With 'HIGH', the 'GPIO' is required to be HIGH in order to switch the 'Base Actor' ON. With 'LOW', the 'GPIO' is required to be LOW in order to switch the 'Base Actor' ON." ) timeout = Property.Number( "Notification duration (ms)", True, 5000, description="0ms will disable notifications completely") actor_shouldbeon = False actor_ison = False actor_flagbackground = False def init(self): GPIO.setup(int(self.dependent_gpio), GPIO.IN, pull_up_down=GPIO.PUD_DOWN) super(GPIODependentActor, self).init() cbpi.GPIODependentActors += [self] def set_power(self, power): self.api.actor_power(int(self.base), power=power) def off(self): self.api.switch_actor_off(int(self.base)) self.actor_shouldbeon = False self.actor_ison = False def on(self, power=None): self.actor_shouldbeon = True if GPIO.input(int(self.dependent_gpio)) == 0: if self.dependency_type == "HIGH": if self.actor_ison: self.api.switch_actor_off(int(self.base)) self.actor_ison = False if self.timeout > 0.0: self.api.notify( "Actor turned off", "Actor turned off because dependent PIN turned LOW", type="warning", timeout=self.timeout) elif not self.actor_flagbackground and self.timeout > 0.0: self.api.notify( "Actor not turned on", "Actor was not turned on because dependent PIN is LOW", type="warning", timeout=self.timeout) elif self.dependency_type == "LOW": if not self.actor_ison: self.api.switch_actor_on(int(self.base), power=power) self.actor_ison = True if self.actor_flagbackground and self.timeout > 0.0: self.api.notify( "Actor turned on", "Actor turned on because dependent PIN turned LOW", type="success", timeout=self.timeout) elif GPIO.input(int(self.dependent_gpio)) == 1: if self.dependency_type == "HIGH": if not self.actor_ison: self.api.switch_actor_on(int(self.base), power=power) self.actor_ison = True if self.actor_flagbackground and self.timeout > 0.0: self.api.notify( "Actor turned on", "Actor turned on because dependent PIN turned HIGH", type="success", timeout=self.timeout) elif self.dependency_type == "LOW": if self.actor_ison: self.api.switch_actor_off(int(self.base)) self.actor_ison = False if self.timeout > 0.0: self.api.notify( "Actor turned off", "Actor turned off because dependent PIN turned HIGH", type="warning", timeout=self.timeout) elif not self.actor_flagbackground and self.timeout > 0.0: self.api.notify( "Actor not turned on", "Actor was not turned on because dependent PIN is HIGH", type="warning", timeout=self.timeout) self.actor_flagbackground = False
class DependentActor(ActorBase): base = Property.Actor( label="Base Actor", description="Select the actor you would like to add a dependency to.") dependency_type = Property.Select( label="Dependency Type", options=["Restriction", "Prerequisite"], description= "Select the dependency type. With 'Restriction', the 'Actor Dependency' is required to be OFF in order to switch the 'Base Actor' ON. With 'Prerequisite', the 'Actor Dependency' is required to be ON in order to switch the 'Base Actor' ON." ) dependency = Property.Actor( label="Actor Dependency", description="Select the actor that the base actor will depend upon.") timeout = Property.Number( "Notification duration (ms)", True, 5000, description="0ms will disable notifications completely") def init(self): # Make sure the Base Actor is off self.api.switch_actor_off(int(self.base)) # Hide Base Actor for idx, value in cbpi.cache["actors"].iteritems(): if idx == int(self.base): value.hide = True # Synchronize current power setting with the Base Actor # TODO def set_power(self, power): self.api.actor_power(int(self.base), power=power) def off(self): self.api.switch_actor_off(int(self.base)) def on(self, power=None): dependency_name = "" for idx, value in cbpi.cache["actors"].iteritems(): if idx == int(self.dependency): dependency_name = value.name for idx, value in cbpi.cache["actors"].iteritems(): if idx == int(self.dependency): if (value.state == 0) & (self.dependency_type == "Restriction"): self.api.switch_actor_on(int(self.base), power=power) elif (value.state == 1) & (self.dependency_type == "Prerequisite"): self.api.switch_actor_on(int(self.base), power=power) else: self.api.switch_actor_off(int(self.base)) if self.timeout > 0.0: self.api.notify( headline="Powering of actor prevented", message= "This is due to the current power state of it's dependency, %s" % (dependency_name), timeout=self.timeout, type="danger") raise UserWarning( "Powering of actor prevented by the state of it's dependency" )
class MQTTListenerControlActor(SensorActive): a_topic = Property.Text("Topic", configurable=True, default_value="", description="MQTT TOPIC") b_payload = Property.Text( "Payload Dictioanry", configurable=True, default_value="", description="Where to find msg in patload, leave blank for raw payload" ) c_unit = Property.Text("Unit", configurable=True, default_value="", description="Units to display") base = Property.Actor( label="Base Actor", description="Select the actor you would like to control from MQTT.") last_value = None def init(self): self.topic = self.a_topic if self.b_payload == "": self.payload_text = None else: self.payload_text = self.b_payload.split('.') self.unit = self.c_unit[0:3] SensorActive.init(self) def on_message(client, userdata, msg): try: print "payload " + msg.payload json_data = json.loads(msg.payload) #print json_data print json_data val = json_data if self.payload_text is not None: for key in self.payload_text: val = val.get(key, None) #print val print val if isinstance(val, (int, float, basestring)): q.put({"id": on_message.sensorid, "value": val}) except Exception as e: print e on_message.sensorid = self.id self.api.cache["mqtt"].client.subscribe(self.topic) self.api.cache["mqtt"].client.message_callback_add( self.topic, on_message) def get_value(self): # Control base actor from MQTT. if (self.last_value == 0): self.api.switch_actor_off(int(self.base)) # self.api.switch_actor_on(int(self.base), power=power) elif (self.last_value == 1): self.api.switch_actor_on(int(self.base)) # self.api.switch_actor_on(int(self.base), power=power) # else: # self.api.switch_actor_off(int(self.base)) return {"value": self.last_value, "unit": self.unit} def get_unit(self): return self.unit def stop(self): self.api.cache["mqtt"].client.unsubscribe(self.topic) SensorActive.stop(self) def execute(self): ''' Active sensor has to handle his own loop :return: ''' self.sleep(5)
class MQTTListenerControlObject(SensorActive): # a_topic = Property.Text("Topic", configurable=True, default_value="", description="MQTT TOPIC") a_topic = "cbpi/homebrewing/uuid/commands" b_payload = Property.Text( "Object payload", configurable=True, default_value="", description="Object in patload, e.g. pump, leave blank for raw payload" ) base = Property.Actor( label="Base Actor", description="Select the actor you would like to control from MQTT.") #logfile f = open("gs.log", "w+") f.write("test\n") f.write(a_topic) f.write("\n") f.close() last_value = None def init(self): self.topic = self.a_topic if self.b_payload == "": self.payload_text = None else: self.payload_text = self.b_payload.split('.') # self.payload_text = self.b_payload SensorActive.init(self) def on_message(client, userdata, msg): try: print "payload " + msg.payload # f.write("payload " + msg.payload\n) json_data = json.loads(msg.payload) #print json_data # f.write(json_data\n) print json_data val = json_data if self.payload_text is not None: for key in self.payload_text: val = val.get(key, None) #print val print val if isinstance(val, (int, float, basestring)): q.put({"id": on_message.sensorid, "value": val}) except Exception as e: print e on_message.sensorid = self.id self.api.cache["mqtt"].client.subscribe(self.topic) self.api.cache["mqtt"].client.message_callback_add( self.topic, on_message) # # f.close() def get_value(self): # Control base actor from MQTT. if (self.last_value == "off"): self.api.switch_actor_off(int(self.base)) elif (self.last_value == "on"): self.api.switch_actor_on(int(self.base)) return {"value": self.last_value} def get_unit(self): return self.unit def stop(self): self.api.cache["mqtt"].client.unsubscribe(self.topic) SensorActive.stop(self) def execute(self): ''' Active sensor has to handle his own loop :return: ''' # f.close() self.sleep(5)
class SimulatedFlowSensor(SensorActive): # properties a_flow_actor_prop = Property.Actor( "Actor", description="The actor this sensor responds to") a_flow_rate_prop = Property.Number("Flow Rate", configurable=True, default_value=2) b_display_prop = Property.Select("Display", options=["volume", "flow"]) c_volume_units_prop = Property.Text("Volume Units", configurable=True, default_value="L", description="Can by anything") d_time_units_prop = Property.Select("Flow Time Units", options=["/s", "/m"]) #------------------------------------------------------------------------------- def init(self): # convert properties to usable attributes try: self.flow_actor = int(self.a_flow_actor_prop) self.flow_rate = float(self.a_flow_rate_prop) except: self.flow_actor = None self.flow_rate = 0.0 self.display_type = self.b_display_prop if self.b_display_prop else 'volume' self.time_units = str(self.d_time_units_prop) self.period_adjust = 1.0 / 60.0 if (self.d_time_units_prop == "/m") else 1.0 self.volume_units = "Units" if self.c_volume_units_prop == "" else self.c_volume_units_prop self.flow_device = None # initialize self.reset_volume() SensorActive.init(self) #------------------------------------------------------------------------------- def execute(self): # at startup, wait for actors to initialze while cbpi.cache.get("actors") is None: self.sleep(5) self.flow_device = cbpi.cache.get("actors").get(self.flow_actor, None) # primary sensor loop while self.is_running(): sensor_data = self.read_sensor_data() self.data_received("{:.2f}".format(sensor_data[self.display_type])) self.sleep(1.0) #------------------------------------------------------------------------------- def read_sensor_data(self): time_now = time.time() if self.flow_device and int(self.flow_device.state): # configured flow rate x actor power level flow = self.flow_rate * (float(self.flow_device.power) / 100.0) # increment by flow rate x elapsed time self._volume += flow * (time_now - self._time_last) * self.period_adjust else: flow = 0.0 self._time_last = time_now return {'count': 0, 'flow': flow, 'volume': self._volume} #------------------------------------------------------------------------------- @cbpi.action("Reset Volume") def reset_volume(self): self._volume = 0.0 self._time_last = time.time() #------------------------------------------------------------------------------- def get_unit(self): unit = self.volume_units if self.display_type == "flow": unit += self.time_units return unit
class RescaledActor(ActorBase): """An actor that wraps a base actor, and rescales the requested power linearly according to the min and max power properties. This allows you to set a maximum power output for an over powered heater or to set a minimum power output for something like a pump that stalls below a certain power setting. CraftBeerPi3 uses integers between 0 and 100 for the power settings for many actors, and so this plugin may cause quantization issues if the range between minimum and maximum power is small. """ base = Property.Actor(label="Base Actor", description="Select the actor you would like to rescale") min_power = Property.Number("Minimum Power (%)", True, 0, description="The minimum output power of the actor when switched on. Use with caution on heaters!") max_power = Property.Number("Maximum Power (%)", True, 100, description="The maximum output power of the actor when switched on.") timeout = Property.Number("Notification duration (ms)", True, 5000, description="0ms will disable notifications completely") def init(self): # Where should we check that maximum is greater than minimum? # using set_power method for now, but it should be when min and max are set # on closing settings dialog pass def set_power(self, power): """Set the power as a percentage of the range between minimum and maximum power""" self.power = power self.api.actor_power(int(self.base), power=self.rescale_power(power)) def rescale_power(self, power): """Calculate the rescaled power from the requested power""" # We carry out the maths in a standalone function so that on and set_power # can use it # If power supplied is None, return our own power, so that base actor is # controlled to our power. If our power hasn't been set yet, set it to 100.0 if power is None: try: power = self.power except AttributeError: self.power = 100. power = 100. min_power = float(self.min_power) max_power = float(self.max_power) if min_power > max_power: self.api.notify(headline="Invalid Settings", message="Minium power is set to a value greater than the maximum power", timeout=self.timeout, type="danger") raise UserWarning("Minimum power is set to a value greater than maximum power") if min_power == max_power: raise UserWarning("Maximum and minimum power are equal") self.api.notify(headline="Invalid Settings", message="Minimum power is set equal to the maximum power", timeout=self.timeout, type="danger") raise UserWarning("Minimum power is set equal to maximum power") rescaled_power = min_power + (max_power - min_power)/100. * power return rescaled_power def off(self): """Switch the actor off""" self.api.switch_actor_off(int(self.base)) def on(self, power=None): """Switch the actor on. Always set the power to the max_power or current power setting.""" self.api.switch_actor_on(int(self.base), power=self.rescale_power(power))
class FunctionActor(ActorBase): global function_actor_ids #Properties a_output_actor = Property.Actor( "Slave Actor", description="Select an Actor to be controlled") b_on_delay = Property.Number("On Delay", configurable=True, default_value=0, description="On wait time in seconds") c_off_delay = Property.Number("Off Delay", configurable=True, default_value=0, description="Off wait time in seconds") d_cycle_delay = Property.Number( "Cycle Delay", configurable=True, default_value=0, description="Minimum time before next turn on in seconds") h_control_word = Property.Text( "Control Func", configurable=True, default_value="", description= "Control function executed when actor switches on, see Readme for more info" ) trigger_sensor_a = Property.Sensor( "Trigger Sensor 1 (S1 or sensor)", description="Select a Sensor to be used as a trigger") trigger_sensor_b = Property.Sensor( "Trigger Sensor 2 (S2)", description="Select a Sensor to be used as a trigger") trigger_text = Property.Text( "Trigger Rule", configurable=True, default_value="True", description="Trigger eqation, use sensor as key word, eg sensor > 25") def init(self): try: cbpi.app.logger.info("Func Actor init") #guards and stored vals self.out = dict.fromkeys( [ "on", "req", "active", "im_on", "im_off", "no_force", "last_on" ], False ) #on: slave state, active: actor state trig: actor is triggered req: slave on is requested self.power = 100 #output timers time_now = datetime.utcnow() self.times = dict.fromkeys(["onoff", "cycle"], time_now) self.delay = {} self.delay["on"] = timedelta(seconds=tryfloat(self.b_on_delay)) self.delay["off"] = timedelta(seconds=tryfloat(self.c_off_delay)) self.delay["cycle"] = timedelta( seconds=tryfloat(self.d_cycle_delay)) self.pulse = { "on_list": [], "off_list": [], "next": [], "loop": False } #remove 'ordering' letter self.control_word = self.h_control_word self.output_actor = self.a_output_actor #check for sensor and trigger config if (((isinstance(self.trigger_sensor_a, unicode) and self.trigger_sensor_a) or (isinstance(self.trigger_sensor_b, unicode) and self.trigger_sensor_b)) and isinstance(self.trigger_text, unicode) and self.trigger_text): self.trig = {"s1": None, "s2": None, "text": self.trigger_text} if isinstance(self.trigger_sensor_a, unicode) and self.trigger_sensor_a: self.trig["s1"] = self.trigger_sensor_a if isinstance(self.trigger_sensor_b, unicode) and self.trigger_sensor_b: self.trig["s2"] = self.trigger_sensor_b self.trig.update( dict.fromkeys(["last", "im_on", "im_off", "type"], False)) else: self.trig = None #check for control word config if isinstance(self.control_word, unicode) and self.control_word: self.control_word = self.decode_control_word() else: self.control_word = None #add actor to list to execute if not int(self.id) in function_actor_ids: function_actor_ids.append(int(self.id)) self.api.switch_actor_off(int(self.output_actor)) self.display_power(0) except Exception as e: print "Function init fail" traceback.print_exc() e.throw def decode_control_word(self): try: #pulse vars word_temp = self.control_word.replace("_", "") word_temp = word_temp.replace("(", "[") word_temp = word_temp.replace(")", "]") words_temp = word_temp.split() for word in words_temp: if word[0] == "P": #Pulse command print "On Pulse command" self.pulse["on_list"] = tuple(eval(word[1:])) self.pulse["loop"] = False elif word[0] == "p": #Pulse command print "Off Pulse command" self.pulse["off_list"] = tuple(eval(word[1:])) elif word[0] == "L": #Pulse command print "Loop command" self.pulse["on_list"] = tuple(eval(word[1:])) self.pulse["loop"] = True elif word[0] == "R": #Ramp command print "On Ramp command not implemented" elif word[0] == "r": #Ramp command print "Off Ramp command not implemented" elif word == "trigSwitchActor": self.trig["type"] = "Sw" elif word == "trigToggleActor": self.trig["type"] = "Tog" elif word == "trigImOn": self.trig["im_on"] = True elif word == "trigImOff": self.trig["im_off"] = True elif word == "UiImOn": self.out["im_on"] = True elif word == "UiImOff": self.out["im_off"] = True elif word == "noForce": self.out["no_force"] = True else: print word raise ValueError("Control word not valid") except Exception as e: print e cbpi.app.logger.error("Control Word not valid") return False return True def trigger_eval(self): #print "trigger" if self.trig["s1"]: sensor = tryfloat(cbpi.get_sensor_value(tryint(self.trig["s1"]))) else: sensor = 0 s1 = sensor if self.trig["s2"]: s2 = tryfloat(cbpi.get_sensor_value(tryint(self.trig["s2"]))) else: s2 = 0 on = self.out["active"] state = self.out["on"] off = not self.out["active"] trig_sig = bool(eval(self.trig["text"])) #based on trigger and immediate settings, enable or disable actor if trig_sig == True: if self.trig["last"] is False: if self.trig["type"] == "Tog": pass #toggle actor self.update_self(self.power, "Tog") elif self.trig["type"] == "Sw": pass # switch actor on self.update_self(self.power, True) if not self.trig["im_on"]: self.times["onoff"] = datetime.utcnow() + self.delay["on"] self.trig["last"] = True return self.out["req"] else: if self.trig["last"] is True: if self.trig["type"] == "NTog": pass #toggle actor on negative switch elif self.trig["type"] == "Sw": pass # switch actor off self.update_self(self.power, False) if not self.trig["im_off"]: self.times["onoff"] = datetime.utcnow() + self.delay["off"] self.trig["last"] = False if self.trig["type"] is False: return False else: return self.out["req"] #should not get here print "Failure: should not be here" return False def execute_func(self): #evaluate trigger if setup was valid if self.trig is not None: trigger = self.trigger_eval() else: trigger = self.out["req"] #evalute active condition right_now = datetime.utcnow() if trigger != self.out["active"]: if (right_now >= self.times["onoff"]) and ( (right_now >= self.times["cycle"]) or not trigger): self.out["active"] = trigger self.out["on"] = trigger if trigger: self.pulse["next"] = list(self.pulse["on_list"]) else: self.pulse["next"] = list(self.pulse["off_list"]) if len(self.pulse["next"]) > 0: self.times["pulse"] = right_now + timedelta( seconds=tryfloat(self.pulse["next"][0])) #set output_actor and change func actor power displayed if ((cbpi.cache.get("actors").get(int(self.output_actor)).state != self.out["on"]) or (self.out["no_force"] == True)): if ((self.out["no_force"] == False) or (self.out["on"] != self.out["last_on"])): self.out["last_on"] = self.out["on"] if self.out["on"] == True: self.api.switch_actor_on(int(self.output_actor), power=self.power) self.display_power(self.power) else: self.api.switch_actor_off(int(self.output_actor)) self.display_power(0) # overwrite displayed power as Zero if slave actor is off if self.out["on"] == False and cbpi.cache.get("actors").get( int(self.id)).power != 0: self.display_power(0) #evalute output pulsing if len(self.pulse["next"]) > 0: if right_now > self.times["pulse"]: self.out["on"] = not self.out["on"] del self.pulse["next"][0] if len(self.pulse["next"]) == 0: if self.pulse["loop"] and self.out["active"]: self.pulse["next"] = list(self.pulse["on_list"]) if len(self.pulse["next"]) > 0: self.times["pulse"] = right_now + timedelta( seconds=tryfloat(self.pulse["next"][0])) else: if not self.out["active"]: self.out["on"] = False if self.out["last_on"]: self.times["cycle"] = right_now + self.delay["cycle"] def on(self, power=None): if power is not None: self.power = power if self.out["req"] is False: self.out["req"] = True if self.out["im_on"] is False: self.times["onoff"] = datetime.utcnow() + self.delay["on"] def off(self): if self.out["req"] == True: self.out["req"] = False if self.out["im_off"] is False: self.times["onoff"] = datetime.utcnow() + self.delay["off"] def set_power(self, power=None): if power is not None: self.power = power self.api.actor_power(int(self.output_actor), power=self.power) def update_self(self, pwr, state): actor = cbpi.cache.get("actors").get(int(self.id)) actor.power = pwr if state == "Tog": state = not actor.state if actor.state != state: actor.state = state self.out["req"] = state cbpi.emit("SWITCH_ACTOR", actor) def display_power(self, pwr): actor = cbpi.cache.get("actors").get(int(self.id)) actor.power = pwr cbpi.emit("SWITCH_ACTOR", actor)