def init(self, force_scan): settings_path = '/Settings/ModbusClient/' + self.name SETTINGS = { 'devices': [settings_path + '/Devices', '', 0, 0], 'autoscan': [settings_path + '/AutoScan', self.auto_scan, 0, 1], } self.dbusconn = private_bus() log.info('Waiting for localsettings') self.settings = SettingsDevice(self.dbusconn, SETTINGS, self.setting_changed, timeout=10) devices = filter(None, self.settings['devices']) self.update_devlist('', devices) if not self.keep_failed: self.failed = [] scan = force_scan if not self.devices or self.failed: if self.settings['autoscan']: scan = True if scan: self.start_scan(force_scan)
def initSettings(newSettings): global settings # settingsDevice is the library class that handles the reading and setting of persistent settings settings = SettingsDevice( bus=dbus.SystemBus() if (platform.machine() == 'armv7l') else dbus.SessionBus(), supportedSettings=newSettings, eventCallback=handle_changed_setting)
def start(self): log.info('Waiting for localsettings') self.settings = SettingsDevice(self.dbus.dbusconn, hiber_settings, self.setting_changed, timeout=10) self.ser = serial.Serial(self.dev, self.rate) self.thread = threading.Thread(target=self.run) self.thread.start()
def __init__(self, n2kfluidtype, n2ktankinstance, paths, productname='Signal K tank', connection='Signal K tank service'): self._dbus = dbusconnection() self._servicename = '%s_%s_%s_%s' % ( APPLICATION_SERVICE_NAME, SIGNALK_SERVER.replace('.', '_').replace( ':', '_'), str(n2kfluidtype), str(n2ktankinstance)) self._dbusservicename = 'com.victronenergy.tank.%s' % self._servicename self._paths = paths # Process settings and recover our VRM instance number appsettingspath = '%s/%s' % (SETTINGS_ROOT, APPLICATION_SERVICE_NAME) servicesettingspath = '%s/%s' % (SETTINGS_ROOT, self._servicename) proposedclassdeviceinstance = '%s:%s' % ('tank', n2ktankinstance) SETTINGS = { 'instance': [ servicesettingspath + '/ClassAndVrmInstance', proposedclassdeviceinstance, 0, 0 ], 'customname': [servicesettingspath + '/CustomName', '', 0, 0] } self._settings = SettingsDevice(self._dbus, SETTINGS, self._handlesettingschanged) self._dbusservice = VeDbusService(self._dbusservicename, self._dbus) self._dbusservice.add_path('/Mgmt/ProcessName', __file__) self._dbusservice.add_path( '/Mgmt/ProcessVersion', VERSION + ' on Python ' + platform.python_version()) self._dbusservice.add_path('/Mgmt/Connection', 'SignalK ' + self._settings['instance']) self._dbusservice.add_path('/DeviceInstance', self._settings['instance'].split(':')[1]) self._dbusservice.add_path('/ProductId', 0) self._dbusservice.add_path('/ProductName', '') self._dbusservice.add_path('/FirmwareVersion', 0) self._dbusservice.add_path('/HardwareVersion', 0) self._dbusservice.add_path('/Connected', 1) for path, settings in self._paths.iteritems(): self._dbusservice.add_path( path, settings['initial'], writeable=True, onchangecallback=self._handlechangedvalue)
def main(): log.info('starting ' + c.DRIVER_NAME) tty = parse_cmdline_args() sensor = ImtSiRs485Sensor(tty) hw_ver, fw_ver = sensor.identify() # add hw and fw version to the signals, they are mandatory signals = c.SIGNALS + [ DbusSignal('/HardwareVersion', hw_ver), DbusSignal('/FirmwareVersion', fw_ver) ] service_name = c.SERVICE_NAME + '.' + tty watchdog_timeout = (c.UPDATE_INTERVAL + c.SERIAL_TIMEOUT) * 2 # starting watchdog here, because with VeDbusServiceAsync we are going multi-threaded with Watchdog(watchdog_timeout) as watchdog, VeDbusServiceAsync( service_name, signals) as dbus: settings_for_tty = get_settings_for_tty(tty) settings = SettingsDevice(dbus.dbusconn, settings_for_tty, None) _is_subsensor_present = partial(is_subsensor_present, settings) # only the modbus signals are updated, the others are const modbus_signals = [s for s in signals if isinstance(s, ModbusSignal)] while True: watchdog.alive() # signal the watchdog that we are still alive registers = sensor.read_modbus_registers() for s in modbus_signals: value = registers[s.reg_no] if s.signed and value & 0x8000: value -= 0x10000 value *= s.gain if _is_subsensor_present(s.dbus_path, value): dbus[s.dbus_path] = value else: dbus[s.dbus_path] = None log.debug('iteration completed, sleeping for ' + str(c.UPDATE_INTERVAL) + ' seconds') sleep(c.UPDATE_INTERVAL)
def setUp(self): self.start_services('vebus') self.start_services('battery') #self.start_services('pvinverter') #self.start_services('solarcharger') self.bus = dbus.SessionBus( ) if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() settings = { attrs[0]: attrs for attrs in [ ['/Settings/System/TimeZone', 'Europe/Amsterdam', 0, 0], ['/Settings/Relay/Polarity', 0, 0, 1], ['/Settings/Relay/Function', 1, 0, 1], [ '/Settings/Generator0/BatteryService', 'com_victronenergy_battery_223/Dc/0', 0, 0 ], ['/Settings/Generator0/QuietHours/Enabled', 0, 0, 1], ['/Settings/Generator0/QuietHours/StartTime', 0, 0, 0], ['/Settings/Generator0/QuietHours/EndTime', 0, 0, 0], ['/Settings/Generator0/MinimumRuntime', 0, 0, 0], ['/Settings/Generator0/TestRun/Enabled', 0, 0, 1], ['/Settings/Generator0/TestRun/RunTillBatteryFull', 0, 0, 1], ['/Settings/Generator0/TestRun/SkipRuntime', 0, 0, 0], ['/Settings/Generator0/TestRun/StartDate', 0, 0, 0], ['/Settings/Generator0/TestRun/StartTime', 0, 0, 0], ['/Settings/Generator0/TestRun/Interval', 0, 0, 0], ['/Settings/Generator0/TestRun/Duration', 0, 0, 0], ['/Settings/Generator0/AutoStartEnabled', 1, 0, 1], ['/Settings/Generator0/OnLossCommunication', 0, 0, 0], ['/Settings/Generator0/AccumulatedDaily', '', 0, 0], ['/Settings/Generator0/AccumulatedTotal', 0, 0, 0], ] } conditions = ("BatteryCurrent", "BatteryVoltage", "AcLoad") condition_defaults = { "StartValue": 0, "StopValue": 0, "StartTimer": 0, "StopTimer": 0, "Enabled": 0, "QuitHoursStartValue": 0, "QuitHoursStopValue": 0 } for condition in conditions: for setting_suffix, default_value in condition_defaults.iteritems( ): setting_label = 'gen0' + condition + setting_suffix setting_path = '/Settings/Generator0/' + condition + '/' + setting_suffix settings[setting_label] = [setting_path, default_value, 0, 0] self.settings = SettingsDevice( bus=self.bus, supportedSettings=settings, eventCallback=self.handle_changed_setting) # dbusgenerator needs to be able to read the timezone setting, so the settings must already by added self.start_services('dbusgenerator') self._settingspath = 'com.victronenergy.settings' self._generatorpath = 'com.victronenergy.generator.startstop0' self.batteryservice = 'com.victronenergy.battery.tty22' self.vebusservice = 'com.victronenergy.vebus.tty23' self.solarchergerservice = 'com.victronenergy.solarcharger.tty33' self.pvinverterservice = 'com.victronenergy.pvinverter.test' self.systemcalcservice = 'com.victronenergy.system' self.retriesonerror = 30 self.set_value(self._settingspath, "/Settings/Relay/Function", 1) self.fill_history() self.firstRun = False self.reset_all_conditons() if (platform.machine() == 'armv7l'): os.environ['TZ'] = self.get_value(self._settingspath, '/Settings/System/TimeZone')
def __init__(self): self.driver_start_time = datetime.now() # data from yaml config file self._cfg = self.get_config_data() _cfg_bms = self._cfg['BMSData'] # TODO: use venus settings to define these values #Initial BMS values eventually read from settings. self._bms_data = BMSData(max_battery_voltage=_cfg_bms['max_battery_voltage'], \ min_battery_voltage=_cfg_bms['min_battery_voltage'], low_battery_voltage=_cfg_bms['low_battery_voltage'], \ charge_bulk_amps=_cfg_bms['charge_bulk_amps'], max_discharge_amps=_cfg_bms['max_discharge_amps'], \ charge_absorb_voltage=_cfg_bms['charge_absorb_voltage'], charge_float_voltage=_cfg_bms['charge_float_voltage'], \ time_min_absorb=_cfg_bms['time_min_absorb'], rebulk_voltage=_cfg_bms['rebulk_voltage']) self.bms_controller = BMSChargeController(charge_bulk_current=self._bms_data.charge_bulk_amps, \ charge_absorb_voltage=self._bms_data.charge_absorb_voltage, charge_float_voltage=self._bms_data.charge_float_voltage, \ time_min_absorb=self._bms_data.time_min_absorb, rebulk_voltage=self._bms_data.rebulk_voltage) ret = self.bms_controller.start_charging() # Have a mainloop, so we can send/receive asynchronous calls to and from dbus DBusGMainLoop(set_as_default=True) self._can_bus = False logger.debug("Can bus init") try : self._can_bus = can.interface.Bus(bustype=canBusType, channel=canBusChannel, bitrate=500000) except can.CanError as e: logger.error(e) logger.debug("Can bus init done") # Add the AcInput1 setting if it doesn't exist so that the grid data is reported # to the system by dbus-systemcalc-py service settings = SettingsDevice( bus=dbus.SystemBus(),# if (platform.machine() == 'armv7l') else dbus.SessionBus(), supportedSettings={ 'acinput': ['/Settings/SystemSetup/AcInput1', 1, 0, 1], }, eventCallback=None) # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. dummy = {'code': None, 'whenToLog': 'configChange', 'accessLevel': None} dbus_tree = {'com.victronenergy.system': {'/Dc/Battery/Soc': dummy, '/Dc/Battery/Current': dummy, '/Dc/Battery/Voltage': dummy, \ '/Dc/Pv/Current': dummy, '/Ac/PvOnOutput/L1/Power': dummy, '/Ac/PvOnOutput/L2/Power': dummy, }} self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed) self._dbusservice = self._create_dbus_service() self._dbusservice.add_path('/Serial', value=12345) # /SystemState/State -> 0: Off # -> 1: Low power # -> 2: VE.Bus Fault condition # -> 3: Bulk charging # -> 4: Absorption charging # -> 5: Float charging # -> 6: Storage mode # -> 7: Equalisation charging # -> 8: Passthru # -> 9: Inverting # -> 10: Assisting # -> 256: Discharging # -> 257: Sustain self._dbusservice.add_path('/State', 0) self._dbusservice.add_path('/Mode', 3) self._dbusservice.add_path('/Ac/PowerMeasurementType', 0) # Create the inverter/charger paths self._dbusservice.add_path('/Ac/Out/L1/P', -1) self._dbusservice.add_path('/Ac/Out/L2/P', -1) self._dbusservice.add_path('/Ac/Out/L1/I', -1) self._dbusservice.add_path('/Ac/Out/L2/I', -1) self._dbusservice.add_path('/Ac/Out/L1/V', -1) self._dbusservice.add_path('/Ac/Out/L2/V', -1) self._dbusservice.add_path('/Ac/Out/L1/F', -1) self._dbusservice.add_path('/Ac/Out/L2/F', -1) self._dbusservice.add_path('/Ac/Out/P', -1) self._dbusservice.add_path('/Ac/ActiveIn/L1/P', -1) self._dbusservice.add_path('/Ac/ActiveIn/L2/P', -1) self._dbusservice.add_path('/Ac/ActiveIn/P', -1) self._dbusservice.add_path('/Ac/ActiveIn/L1/V', -1) self._dbusservice.add_path('/Ac/ActiveIn/L2/V', -1) self._dbusservice.add_path('/Ac/ActiveIn/L1/F', -1) self._dbusservice.add_path('/Ac/ActiveIn/L2/F', -1) self._dbusservice.add_path('/Ac/ActiveIn/L1/I', -1) self._dbusservice.add_path('/Ac/ActiveIn/L2/I', -1) self._dbusservice.add_path('/Ac/ActiveIn/Connected', 1) self._dbusservice.add_path('/Ac/ActiveIn/ActiveInput', 0) self._dbusservice.add_path('/VebusError', 0) self._dbusservice.add_path('/Dc/0/Voltage', -1) self._dbusservice.add_path('/Dc/0/Power', -1) self._dbusservice.add_path('/Dc/0/Current', -1) self._dbusservice.add_path('/Ac/NumberOfPhases', 2) self._dbusservice.add_path('/Alarms/GridLost', 0) # /VebusChargeState <- 1. Bulk # 2. Absorption # 3. Float # 4. Storage # 5. Repeat absorption # 6. Forced absorption # 7. Equalise # 8. Bulk stopped self._dbusservice.add_path('/VebusChargeState', 0) # Some attempts at logging consumption. Float of kwhr since driver start (i think) self._dbusservice.add_path('/Energy/GridToDc', 0) self._dbusservice.add_path('/Energy/GridToAcOut', 0) self._dbusservice.add_path('/Energy/DcToAcOut', 0) self._dbusservice.add_path('/Energy/AcIn1ToInverter', 0) self._dbusservice.add_path('/Energy/AcIn1ToAcOut', 0) self._dbusservice.add_path('/Energy/InverterToAcOut', 0) self._dbusservice.add_path('/Energy/Time', timer()) self._changed = True # create timers (time in msec) gobject.timeout_add(2000, exit_on_error, self._can_bus_txmit_handler) gobject.timeout_add(2000, exit_on_error, self._energy_handler) gobject.timeout_add(20, exit_on_error, self._parse_can_data_handler)
def __init__(self, retries=300): self._bus = dbus.SystemBus() if ( platform.machine() == 'armv7l' or 'DBUS_SESSION_BUS_ADDRESS' not in environ) else dbus.SessionBus() self.RELAY_GPIO_FILE = '/sys/class/gpio/gpio182/value' self.HISTORY_DAYS = 30 # One second per retry self.RETRIES_ON_ERROR = retries self._testrun_soc_retries = 0 self._last_counters_check = 0 self._dbusservice = None self._starttime = 0 self._manualstarttimer = 0 self._last_runtime_update = 0 self._timer_runnning = 0 self._battery_measurement_voltage_import = None self._battery_measurement_current_import = None self._battery_measurement_soc_import = None self._battery_measurement_available = True self._vebusservice_high_temperature_import = None self._vebusservice_overload_import = None self._vebusservice = None self._vebusservice_available = False self._relay_state_import = None self._condition_stack = { 'batteryvoltage': { 'name': 'batteryvoltage', 'reached': False, 'boolean': False, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False, 'retries': 0, 'monitoring': 'battery' }, 'batterycurrent': { 'name': 'batterycurrent', 'reached': False, 'boolean': False, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False, 'retries': 0, 'monitoring': 'battery' }, 'acload': { 'name': 'acload', 'reached': False, 'boolean': False, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False, 'retries': 0, 'monitoring': 'vebus' }, 'inverterhightemp': { 'name': 'inverterhightemp', 'reached': False, 'boolean': True, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False, 'retries': 0, 'monitoring': 'vebus' }, 'inverteroverload': { 'name': 'inverteroverload', 'reached': False, 'boolean': True, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False, 'retries': 0, 'monitoring': 'vebus' }, 'soc': { 'name': 'soc', 'reached': False, 'boolean': False, 'timed': False, 'valid': True, 'enabled': False, 'retries': 0, 'monitoring': 'battery' } } # DbusMonitor expects these values to be there, even though we don need them. So just # add some dummy data. This can go away when DbusMonitor is more generic. dummy = { 'code': None, 'whenToLog': 'configChange', 'accessLevel': None } # TODO: possible improvement: don't use the DbusMonitor it all, since we are only monitoring # a set of static values which will always be available. DbusMonitor watches for services # that come and go, and takes care of automatic signal subscribtions etc. etc: all not necessary # in this use case where we have fixed services names (com.victronenergy.settings, and c # com.victronenergy.system). self._dbusmonitor = DbusMonitor({ 'com.victronenergy.settings': { # This is not our setting so do it here. not in supportedSettings '/Settings/Relay/Function': dummy, '/Settings/Relay/Polarity': dummy, '/Settings/System/TimeZone': dummy, }, 'com.victronenergy.system': { # This is not our setting so do it here. not in supportedSettings '/Ac/Consumption/Total/Power': dummy, '/Ac/PvOnOutput/Total/Power': dummy, '/Ac/PvOnGrid/Total/Power': dummy, '/Ac/PvOnGenset/Total/Power': dummy, '/Dc/Pv/Power': dummy, '/AutoSelectedBatteryMeasurement': dummy, } }, self._dbus_value_changed, self._device_added, self._device_removed) # Set timezone to user selected timezone environ['TZ'] = self._dbusmonitor.get_value( 'com.victronenergy.settings', '/Settings/System/TimeZone') # Connect to localsettings self._settings = SettingsDevice( bus=self._bus, supportedSettings={ 'autostart': ['/Settings/Generator0/AutoStartEnabled', 1, 0, 1], 'accumulateddaily': ['/Settings/Generator0/AccumulatedDaily', '', 0, 0], 'accumulatedtotal': ['/Settings/Generator0/AccumulatedTotal', 0, 0, 0], 'batterymeasurement': ['/Settings/Generator0/BatteryService', "default", 0, 0], 'minimumruntime': ['/Settings/Generator0/MinimumRuntime', 0, 0, 86400], # minutes # On permanent loss of communication: 0 = Stop, 1 = Start, 2 = keep running 'onlosscommunication': ['/Settings/Generator0/OnLossCommunication', 0, 0, 2], # Quiet hours 'quiethoursenabled': ['/Settings/Generator0/QuietHours/Enabled', 0, 0, 1], 'quiethoursstarttime': ['/Settings/Generator0/QuietHours/StartTime', 75600, 0, 86400], 'quiethoursendtime': ['/Settings/Generator0/QuietHours/EndTime', 21600, 0, 86400], # SOC 'socenabled': ['/Settings/Generator0/Soc/Enabled', 0, 0, 1], 'socstart': ['/Settings/Generator0/Soc/StartValue', 90, 0, 100], 'socstop': ['/Settings/Generator0/Soc/StopValue', 90, 0, 100], 'qh_socstart': ['/Settings/Generator0/Soc/QuietHoursStartValue', 90, 0, 100], 'qh_socstop': ['/Settings/Generator0/Soc/QuietHoursStopValue', 90, 0, 100], # Voltage 'batteryvoltageenabled': [ '/Settings/Generator0/BatteryVoltage/Enabled', 0, 0, 1 ], 'batteryvoltagestart': [ '/Settings/Generator0/BatteryVoltage/StartValue', 11.5, 0, 150 ], 'batteryvoltagestop': [ '/Settings/Generator0/BatteryVoltage/StopValue', 12.4, 0, 150 ], 'batteryvoltagestarttimer': [ '/Settings/Generator0/BatteryVoltage/StartTimer', 20, 0, 10000 ], 'batteryvoltagestoptimer': [ '/Settings/Generator0/BatteryVoltage/StopTimer', 20, 0, 10000 ], 'qh_batteryvoltagestart': [ '/Settings/Generator0/BatteryVoltage/QuietHoursStartValue', 11.9, 0, 100 ], 'qh_batteryvoltagestop': [ '/Settings/Generator0/BatteryVoltage/QuietHoursStopValue', 12.4, 0, 100 ], # Current 'batterycurrentenabled': [ '/Settings/Generator0/BatteryCurrent/Enabled', 0, 0, 1 ], 'batterycurrentstart': [ '/Settings/Generator0/BatteryCurrent/StartValue', 10.5, 0.5, 1000 ], 'batterycurrentstop': [ '/Settings/Generator0/BatteryCurrent/StopValue', 5.5, 0, 1000 ], 'batterycurrentstarttimer': [ '/Settings/Generator0/BatteryCurrent/StartTimer', 20, 0, 10000 ], 'batterycurrentstoptimer': [ '/Settings/Generator0/BatteryCurrent/StopTimer', 20, 0, 10000 ], 'qh_batterycurrentstart': [ '/Settings/Generator0/BatteryCurrent/QuietHoursStartValue', 20.5, 0, 1000 ], 'qh_batterycurrentstop': [ '/Settings/Generator0/BatteryCurrent/QuietHoursStopValue', 15.5, 0, 1000 ], # AC load 'acloadenabled': [ '/Settings/Generator0/AcLoad/Enabled', 0, 0, 1 ], 'acloadstart': [ '/Settings/Generator0/AcLoad/StartValue', 1600, 5, 100000 ], 'acloadstop': [ '/Settings/Generator0/AcLoad/StopValue', 800, 0, 100000 ], 'acloadstarttimer': [ '/Settings/Generator0/AcLoad/StartTimer', 20, 0, 10000 ], 'acloadstoptimer': [ '/Settings/Generator0/AcLoad/StopTimer', 20, 0, 10000 ], 'qh_acloadstart': [ '/Settings/Generator0/AcLoad/QuietHoursStartValue', 1900, 0, 100000 ], 'qh_acloadstop': [ '/Settings/Generator0/AcLoad/QuietHoursStopValue', 1200, 0, 100000 ], # VE.Bus high temperature 'inverterhightempenabled': [ '/Settings/Generator0/InverterHighTemp/Enabled', 0, 0, 1 ], 'inverterhightempstarttimer': [ '/Settings/Generator0/InverterHighTemp/StartTimer', 20, 0, 10000 ], 'inverterhightempstoptimer': [ '/Settings/Generator0/InverterHighTemp/StopTimer', 20, 0, 10000 ], # VE.Bus overload 'inverteroverloadenabled': [ '/Settings/Generator0/InverterOverload/Enabled', 0, 0, 1 ], 'inverteroverloadstarttimer': [ '/Settings/Generator0/InverterOverload/StartTimer', 20, 0, 10000 ], 'inverteroverloadstoptimer': [ '/Settings/Generator0/InverterOverload/StopTimer', 20, 0, 10000 ], # TestRun 'testrunenabled': [ '/Settings/Generator0/TestRun/Enabled', 0, 0, 1 ], 'testrunstartdate': [ '/Settings/Generator0/TestRun/StartDate', time.time(), 0, 10000000000.1 ], 'testrunstarttimer': [ '/Settings/Generator0/TestRun/StartTime', 54000, 0, 86400 ], 'testruninterval': [ '/Settings/Generator0/TestRun/Interval', 28, 1, 365 ], 'testrunruntime': [ '/Settings/Generator0/TestRun/Duration', 7200, 1, 86400 ], 'testrunskipruntime': [ '/Settings/Generator0/TestRun/SkipRuntime', 0, 0, 100000 ], 'testruntillbatteryfull': [ '/Settings/Generator0/TestRun/RunTillBatteryFull', 0, 0, 1 ] }, eventCallback=self._handle_changed_setting) # Whenever services come or go, we need to check if it was a service we use. Note that this # is a bit double: DbusMonitor does the same thing. But since we don't use DbusMonitor to # monitor for com.victronenergy.battery, .vebus, .charger or any other possible source of # battery data, it is necessary to monitor for changes in the available dbus services. self._bus.add_signal_receiver(self._dbus_name_owner_changed, signal_name='NameOwnerChanged') self._evaluate_if_we_are_needed() gobject.timeout_add(1000, self._handletimertick) self._update_relay() self._changed = True
def main(): parser = ArgumentParser(description=sys.argv[0]) parser.add_argument( '--servicebase', help='Base service name on dbus, default is com.victronenergy', default='com.victronenergy') parser.add_argument( '--poll', help= 'Use a different kind of polling. Options are epoll, dumb and debug', default='epoll') parser.add_argument('inputs', nargs='+', help='Path to digital input') args = parser.parse_args() PulseCounter = { 'debug': DebugPulseCounter, 'poll': PollingPulseCounter, }.get(args.poll, EpollPulseCounter) DBusGMainLoop(set_as_default=True) # Keep track of enabled services services = {} inputs = dict(enumerate(args.inputs, 1)) pulses = PulseCounter() # callable that iterates over pulses def register_gpio(path, gpio, bus, settings): _type = settings['inputtype'] print("Registering GPIO {} for type {}".format(gpio, _type)) handler = PinHandler.createHandler(_type, bus, args.servicebase, path, gpio, settings) services[gpio] = handler # Only monitor if enabled if _type > 0: handler.level = pulses.register(path, gpio) handler.refresh() def unregister_gpio(gpio): print("unRegistering GPIO {}".format(gpio)) pulses.unregister(gpio) services[gpio].deactivate() def handle_setting_change(inp, setting, old, new): if setting == 'inputtype': if new: # Get current bus and settings objects, to be reused service = services[inp] bus, settings = service.bus, service.settings # Input enabled. If already enabled, unregister the old one first. if pulses.registered(inp): unregister_gpio(inp) # Before registering the new input, reset its settings to defaults settings['count'] = 0 settings['invert'] = 0 settings['invertalarm'] = 0 settings['alarm'] = 0 # Register it register_gpio(inputs[inp], inp, bus, settings) elif old: # Input disabled unregister_gpio(inp) elif setting in ('rate', 'invert', 'alarm', 'invertalarm'): services[inp].refresh() elif setting == 'name': services[inp].product_name = new elif setting == 'count': # Don't want this triggered on a period save, so only execute # if it has changed. v = int(new) s = services[inp] if s.count != v: s.count = v s.refresh() for inp, pth in inputs.items(): supported_settings = { 'inputtype': [ '/Settings/DigitalInput/{}/Type'.format(inp), 0, 0, len(INPUTTYPES) ], 'rate': [ '/Settings/DigitalInput/{}/Multiplier'.format(inp), 0.001, 0, 1.0 ], 'count': ['/Settings/DigitalInput/{}/Count'.format(inp), 0, 0, MAXCOUNT, 1], 'invert': [ '/Settings/DigitalInput/{}/InvertTranslation'.format(inp), 0, 0, 1 ], 'invertalarm': ['/Settings/DigitalInput/{}/InvertAlarm'.format(inp), 0, 0, 1], 'alarm': ['/Settings/DigitalInput/{}/AlarmSetting'.format(inp), 0, 0, 1], 'name': ['/Settings/DigitalInput/{}/CustomName'.format(inp), '', '', ''], } bus = dbusconnection() sd = SettingsDevice(bus, supported_settings, partial(handle_setting_change, inp), timeout=10) register_gpio(pth, inp, bus, sd) def poll(mainloop): from time import time idx = 0 try: for inp, level in pulses(): # epoll object only resyncs once a second. We may receive # a pulse for something that's been deregistered. try: services[inp].toggle(level) except KeyError: continue except: traceback.print_exc() mainloop.quit() # Need to run the gpio polling in separate thread. Pass in the mainloop so # the thread can kill us if there is an exception. mainloop = GLib.MainLoop() poller = Thread(target=lambda: poll(mainloop)) poller.daemon = True poller.start() # Periodically save the counter def save_counters(): for inp in inputs: services[inp].save_count() return True GLib.timeout_add(SAVEINTERVAL, save_counters) # Save counter on shutdown signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) try: mainloop.run() except KeyboardInterrupt: pass finally: save_counters()
def _create_settings(self, *args, **kwargs): bus = dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus() return SettingsDevice(bus, *args, timeout=10, **kwargs)
def __init__(self, servicename, deviceinstance, voltage, capacity, productname='Valence U-BMS', connection='can0'): self.minUpdateDone = 0 self.dailyResetDone = 0 self._bat = UbmsBattery(capacity=capacity, voltage=voltage, connection=connection) self._dbusservice = VeDbusService(servicename + '.socketcan_' + connection + '_di' + str(deviceinstance)) logging.debug("%s /DeviceInstance = %d" % (servicename + '.socketcan_' + connection + '_di' + str(deviceinstance), deviceinstance)) # Create the management objects, as specified in the ccgx dbus-api document self._dbusservice.add_path('/Mgmt/ProcessName', __file__) self._dbusservice.add_path( '/Mgmt/ProcessVersion', VERSION + ' running on Python ' + platform.python_version()) self._dbusservice.add_path('/Mgmt/Connection', connection) # Create the mandatory objects self._dbusservice.add_path('/DeviceInstance', deviceinstance) self._dbusservice.add_path('/ProductId', 0) self._dbusservice.add_path('/ProductName', productname) self._dbusservice.add_path('/FirmwareVersion', 'unknown') self._dbusservice.add_path('/HardwareVersion', 'unknown') self._dbusservice.add_path('/Connected', 0) # Create battery specific objects self._dbusservice.add_path('/Status', 0) self._dbusservice.add_path('/Mode', 1, writeable=True, onchangecallback=self._transmit_mode) self._dbusservice.add_path('/Soh', 100) self._dbusservice.add_path('/Capacity', int(capacity)) self._dbusservice.add_path('/InstalledCapacity', int(capacity)) self._dbusservice.add_path('/Dc/0/Temperature', 25) self._dbusservice.add_path('/Info/MaxChargeCurrent', 70) self._dbusservice.add_path('/Info/MaxDischargeCurrent', 150) self._dbusservice.add_path('/Info/MaxChargeVoltage', float(voltage)) self._dbusservice.add_path('/Info/BatteryLowVoltage', 24.0) self._dbusservice.add_path('/Alarms/CellImbalance', 0) self._dbusservice.add_path('/Alarms/LowVoltage', 0) self._dbusservice.add_path('/Alarms/HighVoltage', 0) self._dbusservice.add_path('/Alarms/HighDischargeCurrent', 0) self._dbusservice.add_path('/Alarms/HighChargeCurrent', 0) self._dbusservice.add_path('/Alarms/LowSoc', 0) self._dbusservice.add_path('/Alarms/LowTemperature', 0) self._dbusservice.add_path('/Alarms/HighTemperature', 0) self._dbusservice.add_path('/Balancing', 0) self._dbusservice.add_path('/System/HasTemperature', 1) self._dbusservice.add_path('/System/NrOfBatteries', 10) self._dbusservice.add_path('/System/NrOfModulesOnline', 10) self._dbusservice.add_path('/System/NrOfModulesOffline', 0) self._dbusservice.add_path('/System/NrOfModulesBlockingDischarge', 0) self._dbusservice.add_path('/System/NrOfModulesBlockingCharge', 0) self._dbusservice.add_path('/System/NrOfBatteriesBalancing', 0) self._dbusservice.add_path('/System/BatteriesParallel', 5) self._dbusservice.add_path('/System/BatteriesSeries', 2) self._dbusservice.add_path('/System/NrOfCellsPerBattery', 4) self._dbusservice.add_path('/System/MinVoltageCellId', 'M_C_') self._dbusservice.add_path('/System/MaxVoltageCellId', 'M_C_') self._dbusservice.add_path('/System/MinCellTemperature', 10.0) self._dbusservice.add_path('/System/MaxCellTemperature', 10.0) self._dbusservice.add_path('/System/MaxPcbTemperature', 10.0) self._settings = SettingsDevice( bus=dbus.SystemBus() if (platform.machine() == 'armv7l') else dbus.SessionBus(), supportedSettings={ 'AvgDischarge': ['/Settings/Ubms/AvgerageDischarge', 0.0, 0, 0], 'TotalAhDrawn': ['/Settings/Ubms/TotalAhDrawn', 0.0, 0, 0], 'TimeLastFull': ['/Settings/Ubms/TimeLastFull', 0.0, 0, 0], 'MinCellVoltage': ['/Settings/Ubms/MinCellVoltage', 4.0, 2.0, 4.0], 'MaxCellVoltage': ['/Settings/Ubms/MaxCellVoltage', 2.0, 2.0, 4.0], 'interval': ['/Settings/Ubms/Interval', 50, 50, 200] }, eventCallback=handle_changed_setting) self._summeditems = { '/System/MaxCellVoltage': { 'gettext': '%.2F V' }, '/System/MinCellVoltage': { 'gettext': '%.2F V' }, '/Dc/0/Voltage': { 'gettext': '%.2F V' }, '/Dc/0/Current': { 'gettext': '%.1F A' }, '/Dc/0/Power': { 'gettext': '%.0F W' }, '/Soc': { 'gettext': '%.0F %%' }, '/History/TotalAhDrawn': { 'gettext': '%.0F Ah' }, '/History/DischargedEnergy': { 'gettext': '%.2F kWh' }, '/History/ChargedEnergy': { 'gettext': '%.2F kWh' }, '/History/AverageDischarge': { 'gettext': '%.2F kWh' }, '/TimeToGo': { 'gettext': '%.0F s' }, '/ConsumedAmphours': { 'gettext': '%.1F Ah' } } for path in self._summeditems.keys(): self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) self._dbusservice['/History/AverageDischarge'] = self._settings[ 'AvgDischarge'] self._dbusservice['/History/TotalAhDrawn'] = self._settings[ 'TotalAhDrawn'] self._dbusservice.add_path('/History/TimeSinceLastFullCharge', 0) self._dbusservice.add_path('/History/MinCellVoltage', self._settings['MinCellVoltage']) self._dbusservice.add_path('/History/MaxCellVoltage', self._settings['MaxCellVoltage']) self._dbusservice['/ConsumedAmphours'] = 0 logging.info( "History cell voltage min: %.3f, max: %.3f, totalAhDrawn: %d", self._settings['MinCellVoltage'], self._settings['MaxCellVoltage'], self._settings['TotalAhDrawn']) self._dbusservice['/History/ChargedEnergy'] = 0 self._dbusservice['/History/DischargedEnergy'] = 0 gobject.timeout_add(self._settings['interval'], exit_on_error, self._update)
def __init__(self, dbusmonitor_gen=None, dbusservice_gen=None, settings_device_gen=None): self.STATE_IDLE = 0 self.STATE_CHARGING = 1 self.STATE_DISCHARGING = 2 self.BATSERVICE_DEFAULT = 'default' self.BATSERVICE_NOBATTERY = 'nobattery' # Why this dummy? Because DbusMonitor expects these values to be there, even though we don't # need them. So just add some dummy data. This can go away when DbusMonitor is more generic. dummy = { 'code': None, 'whenToLog': 'configChange', 'accessLevel': None } dbus_tree = { 'com.victronenergy.solarcharger': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy }, 'com.victronenergy.pvinverter': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Ac/L1/Power': dummy, '/Ac/L2/Power': dummy, '/Ac/L3/Power': dummy, '/Position': dummy, '/ProductId': dummy }, 'com.victronenergy.battery': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy, '/Dc/0/Power': dummy, '/Soc': dummy, '/TimeToGo': dummy, '/ConsumedAmphours': dummy, '/ProductId': dummy }, 'com.victronenergy.vebus': { '/Ac/ActiveIn/ActiveInput': dummy, '/Ac/ActiveIn/L1/P': dummy, '/Ac/ActiveIn/L2/P': dummy, '/Ac/ActiveIn/L3/P': dummy, '/Ac/Out/L1/P': dummy, '/Ac/Out/L2/P': dummy, '/Ac/Out/L3/P': dummy, '/Connected': dummy, '/Hub4/AcPowerSetpoint': dummy, '/ProductId': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Mode': dummy, '/State': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy, '/Dc/0/Power': dummy, '/Soc': dummy }, 'com.victronenergy.charger': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy }, 'com.victronenergy.grid': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/ProductId': dummy, '/DeviceType': dummy, '/Ac/L1/Power': dummy, '/Ac/L2/Power': dummy, '/Ac/L3/Power': dummy }, 'com.victronenergy.genset': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/ProductId': dummy, '/DeviceType': dummy, '/Ac/L1/Power': dummy, '/Ac/L2/Power': dummy, '/Ac/L3/Power': dummy }, 'com.victronenergy.settings': { '/Settings/SystemSetup/AcInput1': dummy, '/Settings/SystemSetup/AcInput2': dummy } } if dbusmonitor_gen is None: self._dbusmonitor = DbusMonitor(dbus_tree, self._dbus_value_changed, self._device_added, self._device_removed) else: self._dbusmonitor = dbusmonitor_gen(dbus_tree) # Connect to localsettings supported_settings = { 'batteryservice': [ '/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0 ], 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], 'writevebussoc': ['/Settings/SystemSetup/WriteVebusSoc', 0, 0, 1] } if settings_device_gen is None: self._settings = SettingsDevice( bus=dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus(), supportedSettings=supported_settings, eventCallback=self._handlechangedsetting) else: self._settings = settings_device_gen(supported_settings, self._handlechangedsetting) # put ourselves on the dbus if dbusservice_gen is None: self._dbusservice = VeDbusService('com.victronenergy.system') else: self._dbusservice = dbusservice_gen('com.victronenergy.system') self._dbusservice.add_mandatory_paths( processname=__file__, processversion=softwareVersion, connection='data from other dbus processes', deviceinstance=0, productid=None, productname=None, firmwareversion=None, hardwareversion=None, connected=1) # At this moment, VRM portal ID is the MAC address of the CCGX. Anyhow, it should be string uniquely # identifying the CCGX. self._dbusservice.add_path('/Serial', value=get_vrm_portal_id(), gettextcallback=lambda x: str(x)) self._dbusservice.add_path('/Relay/0/State', value=None, writeable=True, onchangecallback=lambda p, v: exit_on_error( self._on_relay_state_changed, p, v)) self._dbusservice.add_path('/AvailableBatteryServices', value=None, gettextcallback=self._gettext) self._dbusservice.add_path('/AvailableBatteryMeasurements', value=None) self._dbusservice.add_path('/AutoSelectedBatteryService', value=None, gettextcallback=self._gettext) self._dbusservice.add_path('/AutoSelectedBatteryMeasurement', value=None, gettextcallback=self._gettext) self._dbusservice.add_path('/ActiveBatteryService', value=None, gettextcallback=self._gettext) self._dbusservice.add_path('/PvInvertersProductIds', value=None) self._dbusservice.add_path('/Dc/Battery/Alarms/CircuitBreakerTripped', value=None) self._summeditems = { '/Ac/Grid/L1/Power': { 'gettext': '%.0F W' }, '/Ac/Grid/L2/Power': { 'gettext': '%.0F W' }, '/Ac/Grid/L3/Power': { 'gettext': '%.0F W' }, '/Ac/Grid/Total/Power': { 'gettext': '%.0F W' }, '/Ac/Grid/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/Grid/ProductId': { 'gettext': '%s' }, '/Ac/Grid/DeviceType': { 'gettext': '%s' }, '/Ac/Genset/L1/Power': { 'gettext': '%.0F W' }, '/Ac/Genset/L2/Power': { 'gettext': '%.0F W' }, '/Ac/Genset/L3/Power': { 'gettext': '%.0F W' }, '/Ac/Genset/Total/Power': { 'gettext': '%.0F W' }, '/Ac/Genset/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/Genset/ProductId': { 'gettext': '%s' }, '/Ac/Genset/DeviceType': { 'gettext': '%s' }, '/Ac/Consumption/L1/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/L2/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/L3/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/Total/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/PvOnOutput/L1/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnOutput/L2/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnOutput/L3/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnOutput/Total/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnOutput/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/PvOnGrid/L1/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGrid/L2/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGrid/L3/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGrid/Total/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGrid/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/PvOnGenset/L1/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGenset/L2/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGenset/L3/Power': { 'gettext': '%.0F W' }, '/Ac/PvOnGenset/NumberOfPhases': { 'gettext': '%d' }, '/Ac/PvOnGenset/Total/Power': { 'gettext': '%.0F W' }, '/Dc/Pv/Power': { 'gettext': '%.0F W' }, '/Dc/Pv/Current': { 'gettext': '%.1F A' }, '/Dc/Battery/Voltage': { 'gettext': '%.2F V' }, '/Dc/Battery/Current': { 'gettext': '%.1F A' }, '/Dc/Battery/Power': { 'gettext': '%.0F W' }, '/Dc/Battery/Soc': { 'gettext': '%.0F %%' }, '/Dc/Battery/State': { 'gettext': '%s' }, '/Dc/Battery/TimeToGo': { 'gettext': '%.0F s' }, '/Dc/Battery/ConsumedAmphours': { 'gettext': '%.1F Ah' }, '/Dc/Charger/Power': { 'gettext': '%.0F %%' }, '/Dc/Vebus/Current': { 'gettext': '%.1F A' }, '/Dc/Vebus/Power': { 'gettext': '%.0F W' }, '/Dc/System/Power': { 'gettext': '%.0F W' }, '/Hub': { 'gettext': '%s' }, '/Ac/ActiveIn/Source': { 'gettext': '%s' }, '/VebusService': { 'gettext': '%s' } } for path in self._summeditems.keys(): self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) self._batteryservice = None self._determinebatteryservice() self._supervised = {} self._lg_battery = None if self._batteryservice is None: logger.info("Battery service initialized to None (setting == %s)" % self._settings['batteryservice']) self._changed = True for service, instance in self._dbusmonitor.get_service_list().items(): self._device_added(service, instance, do_service_change=False) self._handleservicechange() self._updatevalues() try: self._relay_file_read = open(relayGpioFile, 'rt') self._relay_file_write = open(relayGpioFile, 'wt') self._update_relay_state() gobject.timeout_add(5000, exit_on_error, self._update_relay_state) except IOError: self._relay_file_read = None self._relay_file_write = None logging.warn('Could not open %s (relay)' % relayGpioFile) self._writeVebusSocCounter = 9 gobject.timeout_add(1000, exit_on_error, self._handletimertick) gobject.timeout_add(60000, exit_on_error, self._process_supervised)
def main(): parser = ArgumentParser(description=sys.argv[0]) parser.add_argument('inputs', nargs='+', help='Path to digital input') args = parser.parse_args() DBusGMainLoop(set_as_default=True) dbusservice = VeDbusService('com.victronenergy.digitalinput') inputs = dict(enumerate(args.inputs, 1)) pulses = EpollPulseCounter() # callable that iterates over pulses def register_gpio(path, gpio, f): print "Registering GPIO {} for function {}".format(gpio, f) dbusservice.add_path('/Count/{}'.format(gpio), value=0) dbusservice['/Count/{}'.format(gpio)] = settings[gpio]['count'] if f == INPUT_FUNCTION_COUNTER: dbusservice.add_path('/Volume/{}'.format(gpio), value=0) dbusservice['/Volume/{}'.format( gpio)] = settings[gpio]['count'] * settings[gpio]['rate'] elif f == INPUT_FUNCTION_ALARM: dbusservice.add_path('/Alarms/{}'.format(gpio), value=0) pulses.register(path, gpio) def unregister_gpio(gpio): print "unRegistering GPIO {}".format(gpio) pulses.unregister(gpio) for pth in ('Count', 'Volume', 'Alarms'): k = '/{}/{}'.format(pth, gpio) if k in dbusservice: del dbusservice[k] # Interface to settings def handle_setting_change(inp, setting, old, new): if setting == 'function': if new: # Input enabled. If already enabled, unregister the old one first. if pulses.registered(inp): unregister_gpio(inp) register_gpio(inputs[inp], inp, int(new)) elif old: # Input disabled unregister_gpio(inp) settings = {} for inp, pth in inputs.items(): supported_settings = { 'function': ['/Settings/DigitalInput/{}/Function'.format(inp), 0, 0, 2], 'rate': [ '/Settings/DigitalInput/{}/LitersPerPulse'.format(inp), 1, 1, 100 ], 'count': ['/Settings/DigitalInput/{}/Count'.format(inp), 0, 0, MAXCOUNT, 1] } settings[inp] = sd = SettingsDevice(dbusservice.dbusconn, supported_settings, partial(handle_setting_change, inp), timeout=10) if sd['function'] > 0: register_gpio(pth, inp, int(sd['function'])) def poll(mainloop): from time import time #stamps = { inp: [0] * 5 for inp in gpios } idx = 0 try: for inp, level in pulses(): function = settings[inp]['function'] # Only increment Count on rising edge. if level: countpath = '/Count/{}'.format(inp) v = (dbusservice[countpath] + 1) % MAXCOUNT dbusservice[countpath] = v if function == INPUT_FUNCTION_COUNTER: dbusservice['/Volume/{}'.format( inp)] = v * settings[inp]['rate'] if function == INPUT_FUNCTION_ALARM: dbusservice['/Alarms/{}'.format(inp)] = bool( level) * 2 # Nasty way of limiting to 0 or 2. except: traceback.print_exc() mainloop.quit() # Need to run the gpio polling in separate thread. Pass in the mainloop so # the thread can kill us if there is an exception. gobject.threads_init() mainloop = gobject.MainLoop() poller = Thread(target=lambda: poll(mainloop)) poller.daemon = True poller.start() # Periodically save the counter def _save_counters(): for inp in inputs: if settings[inp]['function'] > 0: settings[inp]['count'] = dbusservice['/Count/{}'.format(inp)] def save_counters(): _save_counters() gobject.timeout_add(SAVEINTERVAL, save_counters) gobject.timeout_add(SAVEINTERVAL, save_counters) # Save counter on shutdown signal.signal(signal.SIGTERM, lambda *args: sys.exit(0)) try: mainloop.run() except KeyboardInterrupt: pass finally: _save_counters()
def __init__(self): self.RELAY_GPIO_FILE = '/sys/class/gpio/gpio182/value' self.SERVICE_NOBATTERY = 'nobattery' self.SERVICE_NOVEBUS = 'novebus' self.HISTORY_DAYS = 30 self._last_counters_check = 0 self._dbusservice = None self._batteryservice = None self._vebusservice = None self._starttime = 0 self._manualstarttimer = 0 self._last_runtime_update = 0 self.timer_runnning = 0 self._condition_stack = { 'batteryvoltage': { 'name': 'batteryvoltage', 'reached': False, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False }, 'batterycurrent': { 'name': 'batterycurrent', 'reached': False, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False }, 'acload': { 'name': 'acload', 'reached': False, 'timed': True, 'start_timer': 0, 'stop_timer': 0, 'valid': True, 'enabled': False }, 'soc': { 'name': 'soc', 'reached': False, 'timed': False, 'valid': True, 'enabled': False } } # DbusMonitor expects these values to be there, even though we don need them. So just # add some dummy data. This can go away when DbusMonitor is more generic. dummy = { 'code': None, 'whenToLog': 'configChange', 'accessLevel': None } self._dbusmonitor = DbusMonitor( { 'com.victronenergy.vebus': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/State': dummy, '/Ac/Out/P': dummy, '/Dc/I': dummy, '/Dc/V': dummy, '/Soc': dummy }, 'com.victronenergy.battery': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/V': dummy, '/Dc/0/I': dummy, '/Dc/0/P': dummy, '/Soc': dummy }, 'com.victronenergy.settings': { # This is not our setting so do it here. not in supportedSettings '/Settings/Relay/Function': dummy, '/Settings/Relay/Polarity': dummy, '/Settings/System/TimeZone': dummy } }, self._dbus_value_changed, self._device_added, self._device_removed) # Set timezone to user selected timezone environ['TZ'] = self._dbusmonitor.get_value( 'com.victronenergy.settings', '/Settings/System/TimeZone') # Connect to localsettings self._settings = SettingsDevice( bus=dbus.SystemBus() if (platform.machine() == 'armv7l') else dbus.SessionBus(), supportedSettings={ 'autostart': ['/Settings/Generator/AutoStart', 0, 0, 1], 'accumulateddaily': ['/Settings/Generator/AccumulatedDaily', '', 0, 0], 'accumulatedtotal': ['/Settings/Generator/AccumulatedTotal', 0, 0, 0], 'batteryservice': [ '/Settings/Generator/BatteryService', self.SERVICE_NOBATTERY, 0, 0 ], 'vebusservice': [ '/Settings/Generator/VebusService', self.SERVICE_NOVEBUS, 0, 0 ], # Silent mode 'silentmodeenabled': ['/Settings/Generator/SilentMode/Enabled', 0, 0, 1], 'silentmodestarttimer': ['/Settings/Generator/SilentMode/StartTime', 0, 0, 86400], 'silentmodeendtime': ['/Settings/Generator/SilentMode/EndTime', 0, 0, 86400], # SOC 'socenabled': ['/Settings/Generator/Soc/Enabled', 0, 0, 1], 'socstart': ['/Settings/Generator/Soc/StartValue', 90, 0, 100], 'socstop': ['/Settings/Generator/Soc/StopValue', 90, 0, 100], 'em_socstart': ['/Settings/Generator/Soc/EmergencyStartValue', 90, 0, 100], 'em_socstop': ['/Settings/Generator/Soc/EmergencyStopValue', 90, 0, 100], # Voltage 'batteryvoltageenabled': ['/Settings/Generator/BatteryVoltage/Enabled', 0, 0, 1], 'batteryvoltagestart': [ '/Settings/Generator/BatteryVoltage/StartValue', 11.5, 0, 150 ], 'batteryvoltagestop': ['/Settings/Generator/BatteryVoltage/StopValue', 12.4, 0, 150], 'batteryvoltagestarttimer': [ '/Settings/Generator/BatteryVoltage/StartTimer', 20, 0, 10000 ], 'batteryvoltagestoptimer': ['/Settings/Generator/BatteryVoltage/StopTimer', 20, 0, 10000], 'em_batteryvoltagestart': [ '/Settings/Generator/BatteryVoltage/EmergencyStartValue', 11.9, 0, 100 ], 'em_batteryvoltagestop': [ '/Settings/Generator/BatteryVoltage/EmergencyStopValue', 12.4, 0, 100 ], # Current 'batterycurrentenabled': ['/Settings/Generator/BatteryCurrent/Enabled', 0, 0, 1], 'batterycurrentstart': [ '/Settings/Generator/BatteryCurrent/StartValue', 10.5, 0.5, 1000 ], 'batterycurrentstop': ['/Settings/Generator/BatteryCurrent/StopValue', 5.5, 0, 1000], 'batterycurrentstarttimer': [ '/Settings/Generator/BatteryCurrent/StartTimer', 20, 0, 10000 ], 'batterycurrentstoptimer': ['/Settings/Generator/BatteryCurrent/StopTimer', 20, 0, 10000], 'em_batterycurrentstart': [ '/Settings/Generator/BatteryCurrent/EmergencyStartValue', 20.5, 0, 1000 ], 'em_batterycurrentstop': [ '/Settings/Generator/BatteryCurrent/EmergencyStopValue', 15.5, 0, 1000 ], # AC load 'acloadenabled': [ '/Settings/Generator/AcLoad/Enabled', 0, 0, 1 ], 'acloadstart': [ '/Settings/Generator/AcLoad/StartValue', 1600, 5, 100000 ], 'acloadstop': [ '/Settings/Generator/AcLoad/StopValue', 800, 0, 100000 ], 'acloadstarttimer': [ '/Settings/Generator/AcLoad/StartTimer', 20, 0, 10000 ], 'acloadstoptimer': [ '/Settings/Generator/AcLoad/StopTimer', 20, 0, 10000 ], 'em_acloadstart': [ '/Settings/Generator/AcLoad/EmergencyStartValue', 1900, 0, 100000 ], 'em_acloadstop': [ '/Settings/Generator/AcLoad/EmergencyStopValue', 1200, 0, 100000 ], # Maintenance 'maintenanceenabled': [ '/Settings/Generator/Maintenance/Enabled', 0, 0, 1 ], 'maintenancestartdate': [ '/Settings/Generator/Maintenance/StartDate', time.time(), 0, 10000000000.1 ], 'maintenancestarttimer': ['/Settings/Generator/Maintenance/StartTime', 54000, 0, 86400], 'maintenanceinterval': [ '/Settings/Generator/Maintenance/Interval', 28, 1, 365 ], 'maintenanceruntime': [ '/Settings/Generator/Maintenance/Duration', 7200, 1, 86400 ], 'maintenanceskipruntime': [ '/Settings/Generator/Maintenance/SkipRuntime', 0, 0, 100000 ] }, eventCallback=self._handle_changed_setting) self._evaluate_if_we_are_needed() gobject.timeout_add(1000, self._handletimertick) self._changed = True