def __init__(self,mqqthost,nodeid,service,base_topic='emonhub'): self.d = DbusMonitor(datalist.vrmtree, self._value_changed_on_dbus) """ self.ttl = 300 # seconds to keep sending the updates. If no keepAlive received in this # time - stop sending self.lastKeepAliveRcd = int(time.time()) - self.ttl # initialised to when this code launches """ self.ttm = 0 # seconds to gather data before sending, 0 - send immediate, NN - gather # for NN seconds self._last_publish = int(time.time() - 60) # Just an initialisation value, so the first # ttm timeout is _now_ self._gathered_data_timer = None self._gathered_data = {} self._mqtt = mqtt.Client(client_id=get_vrm_portal_id(), clean_session=True, userdata=None) self._mqtt.loop_start() # creates new thread and runs Mqtt.loop_forever() in it. self._mqtt.on_connect = self._on_connect self._mqtt.on_message = self._on_message self._mqtt.connect_async(mqqthost, port=1883, keepalive=60, bind_address="") self._service_name = service self._nodeid = nodeid #node ide expected by emonhub . need to modigy emonhub.conf accordingly self._topic = base_topic message = '{basetopic}/tx/{nodeid}/values' self._publishPath = message.format(basetopic=self._topic,nodeid=self._nodeid)
def __init__(self, mqtt_server=None, ca_cert=None, user=None, passwd=None, dbus_address=None, keep_alive_interval=None, init_broker=False, debug=False): self._dbus_address = dbus_address self._dbus_conn = (dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus()) \ if dbus_address is None \ else dbus.bus.BusConnection(dbus_address) self._dbus_conn.add_signal_receiver(self._dbus_name_owner_changed, signal_name='NameOwnerChanged') self._connected_to_cloud = False # @todo EV Get portal ID from com.victronenergy.system? self._system_id = get_vrm_portal_id() # Key: D-BUS Service + path, value: topic self._topics = {} # Key: topic, value: last value seen on D-Bus self._values = {} # Key: service_type/device_instance, value: D-Bus service name self._services = {} # Key: short D-Bus service name (eg. 1:31), value: full D-Bus service name (eg. com.victronenergy.settings) self._service_ids = {} # A queue of value changes, so that we may rate-limit this somewhat self.queue = OrderedDict() gobject.timeout_add(1000, self._timer_service_queue) self._last_queue_run = 0 if init_broker: self._registrator = MosquittoBridgeRegistrator(self._system_id) self._registrator.register() else: self._registrator = None self._dbus_conn.add_signal_receiver( self._on_dbus_value_changed, dbus_interface='com.victronenergy.BusItem', signal_name='PropertiesChanged', path_keyword='path', sender_keyword='service_id') services = self._dbus_conn.list_names() for service in services: if service.startswith('com.victronenergy.'): self._service_ids[self._dbus_conn.get_name_owner( service)] = service self._scan_dbus_service(service, publish=False) # Bus scan may take a log time, so start keep alive after scan self._keep_alive_interval = keep_alive_interval self._keep_alive_timer = None MqttGObjectBridge.__init__(self, mqtt_server, "ve-dbus-mqtt-py", ca_cert, user, passwd, debug)
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}, '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, '/Hub4/AcPowerSetpoint': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy, '/Dc/0/Power': dummy, '/Soc': dummy}, 'com.victronenergy.charger': { '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy}, 'com.victronenergy.grid' : { '/ProductName': dummy, '/Mgmt/Connection': dummy, '/ProductId' : dummy, '/DeviceType' : dummy, '/Ac/L1/Power': dummy, '/Ac/L2/Power': dummy, '/Ac/L3/Power': dummy}, 'com.victronenergy.genset' : { '/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()) self._dbusservice.add_path( '/AvailableBatteryServices', value=None, gettextcallback=self._gettext) self._dbusservice.add_path( '/AvailableBatteryMeasurements', value=None, gettextcallback=self._gettext) 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._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() 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() self._writeVebusSocCounter = 9 gobject.timeout_add(1000, exit_on_error, self._handletimertick)
def __init__(self): 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, '/Dc/0/Temperature': dummy, '/FirmwareVersion': 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, '/DeviceInstance': dummy, '/Dc/0/Voltage': dummy, '/Dc/1/Voltage': dummy, '/Dc/0/Current': dummy, '/Dc/0/Power': dummy, '/Dc/0/Temperature': dummy, '/Soc': dummy, '/TimeToGo': dummy, '/ConsumedAmphours': dummy, '/ProductId': dummy, '/CustomName': 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, '/ProductId': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Mode': dummy, '/State': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy, '/Dc/0/Power': dummy, '/Dc/0/Temperature': dummy, '/Soc': dummy}, 'com.victronenergy.charger': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Dc/0/Current': dummy, '/Dc/1/Voltage': dummy, '/Dc/1/Current': dummy, '/Dc/2/Voltage': dummy, '/Dc/2/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, '/Settings/CGwacs/RunWithoutGridMeter' : dummy, '/Settings/System/TimeZone' : dummy}, 'com.victronenergy.temperature': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Temperature': dummy, '/TemperatureType': dummy}, 'com.victronenergy.inverter': { '/Connected': dummy, '/ProductName': dummy, '/Mgmt/Connection': dummy, '/Dc/0/Voltage': dummy, '/Ac/Out/L1/P': dummy, '/Ac/Out/L1/V': dummy, '/Ac/Out/L1/I': dummy, } } self._modules = [ delegates.HubTypeSelect(), delegates.VebusSocWriter(), delegates.ServiceMapper(), delegates.RelayState(), delegates.BuzzerControl(), delegates.LgCircuitBreakerDetect(), delegates.Dvcc(), delegates.BatterySense(), delegates.SystemState(), delegates.BatteryLife(), delegates.ScheduledCharging(), delegates.SourceTimers(), delegates.BatteryData()] for m in self._modules: for service, paths in m.get_input(): s = dbus_tree.setdefault(service, {}) for path in paths: s[path] = dummy self._dbusmonitor = self._create_dbus_monitor(dbus_tree, valueChangedCallback=self._dbus_value_changed, deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) # Connect to localsettings supported_settings = { 'batteryservice': ['/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0], 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1]} for m in self._modules: for setting in m.get_settings(): supported_settings[setting[0]] = list(setting[1:]) self._settings = self._create_settings(supported_settings, self._handlechangedsetting) self._dbusservice = self._create_dbus_service() for m in self._modules: m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) # 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()) 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( '/AutoSelectedTemperatureService', value=None, gettextcallback=self._gettext) self._dbusservice.add_path( '/PvInvertersProductIds', 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/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/NumberOfPhases': {'gettext': '%.0F W'}, '/Ac/Genset/ProductId': {'gettext': '%s'}, '/Ac/Genset/DeviceType': {'gettext': '%s'}, '/Ac/ConsumptionOnOutput/NumberOfPhases': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnOutput/L1/Power': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnOutput/L2/Power': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnOutput/L3/Power': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnInput/NumberOfPhases': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnInput/L1/Power': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnInput/L2/Power': {'gettext': '%.0F W'}, '/Ac/ConsumptionOnInput/L3/Power': {'gettext': '%.0F W'}, '/Ac/Consumption/NumberOfPhases': {'gettext': '%.0F W'}, '/Ac/Consumption/L1/Power': {'gettext': '%.0F W'}, '/Ac/Consumption/L2/Power': {'gettext': '%.0F W'}, '/Ac/Consumption/L3/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/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/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'}, '/Dc/Pv/Power': {'gettext': '%.0F W'}, '/Dc/Pv/Current': {'gettext': '%.1F A'}, '/Dc/Battery/Voltage': {'gettext': '%.2F V'}, '/Dc/Battery/Temperature': {'gettext': '%.1F C'}, '/Dc/Battery/VoltageService': {'gettext': '%s'}, '/Dc/Battery/TemperatureService': {'gettext': '%s'}, '/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'}, '/Ac/ActiveIn/Source': {'gettext': '%s'}, '/VebusService': {'gettext': '%s'} } for m in self._modules: self._summeditems.update(m.get_output()) for path in self._summeditems.keys(): self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) self._batteryservice = None self._determinebatteryservice() 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() gobject.timeout_add(1000, exit_on_error, self._handletimertick)
def __init__(self): 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, '/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, '/Settings/CGwacs/RunWithoutGridMeter': dummy } } service_supervisor = delegates.ServiceSupervisor() self._modules = [ delegates.HubTypeSelect(), delegates.VebusSocWriter(), delegates.ServiceMapper(), service_supervisor, delegates.RelayState(), delegates.BuzzerControl(), delegates.LgCircuitBreakerDetect(), delegates.Hub1Bridge(service_supervisor) ] for m in self._modules: for service, paths in m.get_input(): s = dbus_tree.setdefault(service, {}) for path in paths: s[path] = dummy self._dbusmonitor = self._create_dbus_monitor( dbus_tree, valueChangedCallback=self._dbus_value_changed, deviceAddedCallback=self._device_added, deviceRemovedCallback=self._device_removed) # Connect to localsettings supported_settings = { 'batteryservice': [ '/Settings/SystemSetup/BatteryService', self.BATSERVICE_DEFAULT, 0, 0 ], 'hasdcsystem': ['/Settings/SystemSetup/HasDcSystem', 0, 0, 1], 'useacout': ['/Settings/SystemSetup/HasAcOutSystem', 1, 0, 1] } for m in self._modules: for setting in m.get_settings(): supported_settings[setting[0]] = list(setting[1:]) self._settings = self._create_settings(supported_settings, self._handlechangedsetting) self._dbusservice = self._create_dbus_service() for m in self._modules: m.set_sources(self._dbusmonitor, self._settings, self._dbusservice) # 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()) 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._summeditems = { '/Ac/Grid/L1/Power': { 'gettext': '%.0F W' }, '/Ac/Grid/L2/Power': { 'gettext': '%.0F W' }, '/Ac/Grid/L3/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/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/Genset/ProductId': { 'gettext': '%s' }, '/Ac/Genset/DeviceType': { 'gettext': '%s' }, '/Ac/ConsumptionOnOutput/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnOutput/L1/Power': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnOutput/L2/Power': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnOutput/L3/Power': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnInput/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnInput/L1/Power': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnInput/L2/Power': { 'gettext': '%.0F W' }, '/Ac/ConsumptionOnInput/L3/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/NumberOfPhases': { 'gettext': '%.0F W' }, '/Ac/Consumption/L1/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/L2/Power': { 'gettext': '%.0F W' }, '/Ac/Consumption/L3/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/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/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' }, '/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' }, '/Ac/ActiveIn/Source': { 'gettext': '%s' }, '/VebusService': { 'gettext': '%s' } } for m in self._modules: self._summeditems.update(m.get_output()) for path in self._summeditems.keys(): self._dbusservice.add_path(path, value=None, gettextcallback=self._gettext) self._batteryservice = None self._determinebatteryservice() 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() gobject.timeout_add(1000, exit_on_error, self._handletimertick)
def __init__(self, mqtt_server=None, ca_cert=None, user=None, passwd=None, dbus_address=None, keep_alive_interval=None, init_broker=False): self._ca_cert = ca_cert self._mqtt_user = user self._mqtt_passwd = passwd self._mqtt_server = mqtt_server or '127.0.0.1' self._dbus_address = dbus_address self._dbus_conn = (dbus.SessionBus() if 'DBUS_SESSION_BUS_ADDRESS' in os.environ else dbus.SystemBus()) \ if dbus_address == None \ else dbus.bus.BusConnection(dbus_address) self._dbus_conn.add_signal_receiver(self._dbus_name_owner_changed, signal_name='NameOwnerChanged') # @todo EV Get portal ID from com.victronenergy.system? self._system_id = get_vrm_portal_id() # Key: D-BUS Service + path, value: topic self._topics = {} # Key: topic, value: last value seen on D-Bus self._values = {} # Key: service_type/device_instance, value: D-Bus service name self._services = {} # Key: short D-Bus service name (eg. 1:31), value: full D-Bus service name (eg. com.victronenergy.settings) self._service_ids = {} if init_broker and self._init_broker(): gobject.timeout_add_seconds(60, exit_on_error, self._init_broker) self._dbus_conn.add_signal_receiver( self._on_dbus_value_changed, dbus_interface='com.victronenergy.BusItem', signal_name='PropertiesChanged', path_keyword='path', sender_keyword='service_id') services = self._dbus_conn.list_names() for service in services: if service.startswith('com.victronenergy.'): self._service_ids[self._dbus_conn.get_name_owner( service)] = service self._scan_dbus_service(service, publish=False) # Bus scan may take a log time, so start keep alive after scan self._keep_alive_interval = keep_alive_interval if self._keep_alive_interval != None: self._keep_alive_timer = gobject.timeout_add_seconds( self._keep_alive_interval, exit_on_error, self._on_keep_alive_timeout) self._client = paho.mqtt.client.Client(client_id="ve/dbus-mqtt-py") self._client.on_connect = self._on_connect self._client.on_message = self._on_message self._client.on_disconnect = self._on_disconnect self._socket_watch = None self._socket_timer = None if self._init_mqtt(): gobject.timeout_add_seconds(5, exit_on_error, self._init_mqtt)
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)