|  | @ -81,8 +81,8 @@ def main_loop(ahoy_config): | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     sunset = SunsetHandler(ahoy_config.get('sunset')) |  |  |     sunset = SunsetHandler(ahoy_config.get('sunset')) | 
			
		
	
		
		
			
				
					|  |  |     dtu_ser = ahoy_config.get('dtu', {}).get('serial') |  |  |     dtu_ser = ahoy_config.get('dtu', {}).get('serial') | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     loop_interval = ahoy_config.get('interval', 1) |  |  |     loop_interval = ahoy_config.get('interval', 1) | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     try: |  |  |     try: | 
			
		
	
		
		
			
				
					|  |  |         do_init = True |  |  |         do_init = True | 
			
		
	
		
		
			
				
					|  |  |         while True: |  |  |         while True: | 
			
		
	
	
		
		
			
				
					|  | @ -92,7 +92,7 @@ def main_loop(ahoy_config): | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |             for inverter in inverters: |  |  |             for inverter in inverters: | 
			
		
	
		
		
			
				
					|  |  |                 if hoymiles.HOYMILES_DEBUG_LOGGING: |  |  |                 if hoymiles.HOYMILES_DEBUG_LOGGING: | 
			
		
	
		
		
			
				
					
					|  |  |                     logging.debug(f'Poll inverter {inverter["serial"]}') |  |  |                     logging.info(f'Poll inverter name={inverter["name"]} ser={inverter["serial"]}') | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |                 poll_inverter(inverter, dtu_ser, do_init, 3) |  |  |                 poll_inverter(inverter, dtu_ser, do_init, 3) | 
			
		
	
		
		
			
				
					|  |  |             do_init = False |  |  |             do_init = False | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -161,6 +161,8 @@ def poll_inverter(inverter, dtu_ser, do_init, retries): | 
			
		
	
		
		
			
				
					|  |  |             c_datetime = datetime.now() |  |  |             c_datetime = datetime.now() | 
			
		
	
		
		
			
				
					|  |  |             if hoymiles.HOYMILES_DEBUG_LOGGING: |  |  |             if hoymiles.HOYMILES_DEBUG_LOGGING: | 
			
		
	
		
		
			
				
					|  |  |                 logging.debug(f'{c_datetime} Payload: ' + hoymiles.hexify_payload(response)) |  |  |                 logging.debug(f'{c_datetime} Payload: ' + hoymiles.hexify_payload(response)) | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             # prepare decoder object | 
			
		
	
		
		
			
				
					|  |  |             decoder = hoymiles.ResponseDecoder(response, |  |  |             decoder = hoymiles.ResponseDecoder(response, | 
			
		
	
		
		
			
				
					|  |  |                     request=com.request, |  |  |                     request=com.request, | 
			
		
	
		
		
			
				
					|  |  |                     inverter_ser=inverter_ser, |  |  |                     inverter_ser=inverter_ser, | 
			
		
	
	
		
		
			
				
					|  | @ -168,20 +170,22 @@ def poll_inverter(inverter, dtu_ser, do_init, retries): | 
			
		
	
		
		
			
				
					|  |  |                     dtu_ser=dtu_ser, |  |  |                     dtu_ser=dtu_ser, | 
			
		
	
		
		
			
				
					|  |  |                     strings=inverter_strings |  |  |                     strings=inverter_strings | 
			
		
	
		
		
			
				
					|  |  |                     ) |  |  |                     ) | 
			
		
	
		
		
			
				
					|  |  |             result = decoder.decode() |  |  |  | 
			
		
	
		
		
			
				
					|  |  |             if isinstance(result, hoymiles.decoders.StatusResponse): |  |  |  | 
			
		
	
		
		
			
				
					|  |  |                 data = result.__dict__() |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             # get decoder object | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             result = decoder.decode() | 
			
		
	
		
		
			
				
					|  |  |             if hoymiles.HOYMILES_DEBUG_LOGGING: |  |  |             if hoymiles.HOYMILES_DEBUG_LOGGING: | 
			
		
	
		
		
			
				
					|  |  |                logging.debug(f'{c_datetime} Decoded: {result.__dict__()}') |  |  |                logging.debug(f'{c_datetime} Decoded: {result.__dict__()}') | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             # check decoder object for output | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             if isinstance(result, hoymiles.decoders.StatusResponse): | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 data = result.__dict__() | 
			
		
	
		
		
			
				
					|  |  |                 if 'event_count' in data: |  |  |                 if 'event_count' in data: | 
			
		
	
		
		
			
				
					|  |  |                     if event_message_index[inv_str] < data['event_count']: |  |  |                     if event_message_index[inv_str] < data['event_count']: | 
			
		
	
		
		
			
				
					|  |  |                         event_message_index[inv_str] = data['event_count'] |  |  |                         event_message_index[inv_str] = data['event_count'] | 
			
		
	
		
		
			
				
					|  |  |                         command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.AlarmData, alarm_id=event_message_index[inv_str])) |  |  |                         command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.AlarmData, alarm_id=event_message_index[inv_str])) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |                 if mqtt_client: |  |  |                 if mqtt_client: | 
			
		
	
		
		
			
				
					|  |  |                     # mqtt_send_status(mqtt_client, inverter_ser, data, topic=inverter.get('mqtt', {}).get('topic', None)) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |                    mqtt_client.store_status(result, topic=inverter.get('mqtt', {}).get('topic', None)) |  |  |                    mqtt_client.store_status(result, topic=inverter.get('mqtt', {}).get('topic', None)) | 
			
		
	
		
		
			
				
					|  |  |                      |  |  |                      | 
			
		
	
		
		
			
				
					|  |  |                 if influx_client: |  |  |                 if influx_client: | 
			
		
	
	
		
		
			
				
					|  | @ -190,49 +194,11 @@ def poll_inverter(inverter, dtu_ser, do_init, retries): | 
			
		
	
		
		
			
				
					|  |  |                 if volkszaehler_client: |  |  |                 if volkszaehler_client: | 
			
		
	
		
		
			
				
					|  |  |                    volkszaehler_client.store_status(result) |  |  |                    volkszaehler_client.store_status(result) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  | def mqtt_send_status(broker, inverter_ser, data, topic=None): |  |  |             # check decoder object for output | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |     """ |  |  |             if isinstance(result, hoymiles.decoders.HardwareInfoResponse): | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |     Publish StatusResponse object |  |  |                 if mqtt_client: | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  | 
 |  |  |                    mqtt_client.store_status(result, topic=inverter.get('mqtt', {}).get('topic', None)) | 
			
				
				
			
		
	
		
		
			
				
					|  |  |     :param paho.mqtt.client.Client broker: mqtt-client instance |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     :param str inverter_ser: inverter serial |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     :param hoymiles.StatusResponse data: decoded inverter StatusResponse |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     :param topic: custom mqtt topic prefix (default: hoymiles/{inverter_ser}) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     :type topic: str |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     """ |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     if not topic: |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         topic = f'hoymiles/{inverter_ser}' |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     # Global Head |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if data['time'] is not None: |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/time', data['time'].strftime("%d.%m.%y - %H:%M:%S")) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |      |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     # AC Data |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     phase_id = 0 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     for phase in data['phases']: |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter/{phase_id}/power', phase['power']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter/{phase_id}/voltage', phase['voltage']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter/{phase_id}/current', phase['current']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter/{phase_id}/Q_AC', phase['reactive_power']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         phase_id = phase_id + 1 |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     # DC Data |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     string_id = 0 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     for string in data['strings']: |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter-dc/{string_id}/voltage', string['voltage']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter-dc/{string_id}/current', string['current']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter-dc/{string_id}/power', string['power']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter-dc/{string_id}/YieldDay', string['energy_daily']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/emeter-dc/{string_id}/YieldTotal', string['energy_total']/1000) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         string_id = string_id + 1 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     # Global |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if data['powerfactor'] is not None: |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/pf', data['powerfactor']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     broker.publish(f'{topic}/frequency', data['frequency']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     broker.publish(f'{topic}/temperature', data['temperature']) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if data['energy_total'] is not None: |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         broker.publish(f'{topic}/total', data['energy_total']/1000) |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | def mqtt_on_command(client, userdata, message): |  |  | def mqtt_on_command(client, userdata, message): | 
			
		
	
		
		
			
				
					|  |  |     """ |  |  |     """ | 
			
		
	
	
		
		
			
				
					|  | @ -321,29 +287,27 @@ if __name__ == '__main__': | 
			
		
	
		
		
			
				
					|  |  |         logging.error('Failed to load config file {global_config.config_file}: {e_yaml}') |  |  |         logging.error('Failed to load config file {global_config.config_file}: {e_yaml}') | 
			
		
	
		
		
			
				
					|  |  |         sys.exit(1) |  |  |         sys.exit(1) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     # read AHOY configuration file and prepare logging | 
			
		
	
		
		
			
				
					|  |  |     ahoy_config = dict(cfg.get('ahoy', {})) |  |  |     ahoy_config = dict(cfg.get('ahoy', {})) | 
			
		
	
		
		
			
				
					|  |  |     init_logging(ahoy_config) |  |  |     init_logging(ahoy_config) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     # Prepare for multiple transceivers, makes them configurable (currently |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     # only one supported) |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     for radio_config in ahoy_config.get('nrf', [{}]): |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         hmradio = hoymiles.HoymilesNRF(**radio_config) |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     event_message_index = {} |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     command_queue = {} |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     mqtt_command_topic_subs = [] |  |  |  | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     if global_config.log_transactions: |  |  |     if global_config.log_transactions: | 
			
		
	
		
		
			
				
					|  |  |         hoymiles.HOYMILES_TRANSACTION_LOGGING=True |  |  |         hoymiles.HOYMILES_TRANSACTION_LOGGING=True | 
			
		
	
		
		
			
				
					|  |  |     if global_config.verbose: |  |  |     if global_config.verbose: | 
			
		
	
		
		
			
				
					|  |  |         hoymiles.HOYMILES_DEBUG_LOGGING=True |  |  |         hoymiles.HOYMILES_DEBUG_LOGGING=True | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     # Prepare for multiple transceivers, makes them configurable | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     for radio_config in ahoy_config.get('nrf', [{}]): | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         hmradio = hoymiles.HoymilesNRF(**radio_config) | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     # create MQTT - client object | 
			
		
	
		
		
			
				
					|  |  |     mqtt_client = None |  |  |     mqtt_client = None | 
			
		
	
		
		
			
				
					|  |  |     mqtt_config = ahoy_config.get('mqtt', {}) |  |  |     mqtt_config = ahoy_config.get('mqtt', {}) | 
			
		
	
		
		
			
				
					|  |  |     if mqtt_config and not mqtt_config.get('disabled', False): |  |  |     if mqtt_config and not mqtt_config.get('disabled', False): | 
			
		
	
		
		
			
				
					|  |  |        from .outputs import MqttOutputPlugin |  |  |        from .outputs import MqttOutputPlugin | 
			
		
	
		
		
			
				
					|  |  |        mqtt_client = MqttOutputPlugin(mqtt_config) |  |  |        mqtt_client = MqttOutputPlugin(mqtt_config) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     # create INFLUX - client object | 
			
		
	
		
		
			
				
					|  |  |     influx_client = None |  |  |     influx_client = None | 
			
		
	
		
		
			
				
					|  |  |     influx_config = ahoy_config.get('influxdb', {}) |  |  |     influx_config = ahoy_config.get('influxdb', {}) | 
			
		
	
		
		
			
				
					|  |  |     if influx_config and not influx_config.get('disabled', False): |  |  |     if influx_config and not influx_config.get('disabled', False): | 
			
		
	
	
		
		
			
				
					|  | @ -355,23 +319,24 @@ if __name__ == '__main__': | 
			
		
	
		
		
			
				
					|  |  |                 bucket=influx_config.get('bucket', None), |  |  |                 bucket=influx_config.get('bucket', None), | 
			
		
	
		
		
			
				
					|  |  |                 measurement=influx_config.get('measurement', 'hoymiles')) |  |  |                 measurement=influx_config.get('measurement', 'hoymiles')) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     # create VOLKSZAEHLER - client object | 
			
		
	
		
		
			
				
					|  |  |     volkszaehler_client = None |  |  |     volkszaehler_client = None | 
			
		
	
		
		
			
				
					|  |  |     volkszaehler_config = ahoy_config.get('volkszaehler', {}) |  |  |     volkszaehler_config = ahoy_config.get('volkszaehler', {}) | 
			
		
	
		
		
			
				
					|  |  |     if volkszaehler_config and not volkszaehler_config.get('disabled', False): |  |  |     if volkszaehler_config and not volkszaehler_config.get('disabled', False): | 
			
		
	
		
		
			
				
					|  |  |         from .outputs import VolkszaehlerOutputPlugin |  |  |         from .outputs import VolkszaehlerOutputPlugin | 
			
		
	
		
		
			
				
					
					|  |  |         volkszaehler_client = VolkszaehlerOutputPlugin( |  |  |         volkszaehler_client = VolkszaehlerOutputPlugin(volkszaehler_config) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                 volkszaehler_config) |  |  | 
 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |     event_message_index = {} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     command_queue = {} | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     mqtt_command_topic_subs = [] | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     g_inverters = [g_inverter.get('serial') for g_inverter in ahoy_config.get('inverters', [])] |  |  |  | 
			
		
	
		
		
			
				
					|  |  |     for g_inverter in ahoy_config.get('inverters', []): |  |  |     for g_inverter in ahoy_config.get('inverters', []): | 
			
		
	
		
		
			
				
					|  |  |         g_inverter_ser = g_inverter.get('serial') |  |  |         g_inverter_ser = g_inverter.get('serial') | 
			
		
	
		
		
			
				
					|  |  |         inv_str = str(g_inverter_ser) |  |  |         inv_str = str(g_inverter_ser) | 
			
		
	
		
		
			
				
					|  |  |         command_queue[inv_str] = [] |  |  |         command_queue[inv_str] = [] | 
			
		
	
		
		
			
				
					|  |  |         event_message_index[inv_str] = 0 |  |  |         event_message_index[inv_str] = 0 | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |         # |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         # Enables and subscribe inverter to mqtt /command-Topic |  |  |         # Enables and subscribe inverter to mqtt /command-Topic | 
			
		
	
		
		
			
				
					|  |  |         # |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         if mqtt_client and g_inverter.get('mqtt', {}).get('send_raw_enabled', False): |  |  |         if mqtt_client and g_inverter.get('mqtt', {}).get('send_raw_enabled', False): | 
			
		
	
		
		
			
				
					|  |  |             topic_item = ( |  |  |             topic_item = ( | 
			
		
	
		
		
			
				
					|  |  |                     str(g_inverter_ser), |  |  |                     str(g_inverter_ser), | 
			
		
	
	
		
		
			
				
					|  | @ -380,5 +345,6 @@ if __name__ == '__main__': | 
			
		
	
		
		
			
				
					|  |  |             mqtt_client.subscribe(topic_item[1]) |  |  |             mqtt_client.subscribe(topic_item[1]) | 
			
		
	
		
		
			
				
					|  |  |             mqtt_command_topic_subs.append(topic_item) |  |  |             mqtt_command_topic_subs.append(topic_item) | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     logging.info(f'Starting main_loop with inverter(s) {g_inverters}') |  |  |     # start main-loop | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |     main_loop(ahoy_config) |  |  |     main_loop(ahoy_config) | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | 
 |