From a3839ab74222d189a483e485a38716aa897db680 Mon Sep 17 00:00:00 2001 From: Leo Winter Date: Sat, 10 Sep 2022 20:39:51 +0200 Subject: [PATCH 01/11] Fix link in README.md to latest esp8266/README Update link in README.md to the latest /tools/esp8266/README.md#things-needed path. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 697a6801..2aa5839f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ List of approaches - [Others, C/C++](tools/nano/NRF24_SendRcv/) ## Quick Start with ESP8266 -- [Go here ✨](https://github.com/grindylow/ahoy/blob/ahoy_v0.5.16/tools/esp8266/README.md#things-needed) +- [Go here ✨](https://github.com/grindylow/ahoy/blob/main/tools/esp8266/README.md#things-needed) ## Success Stories From 99b28b8c8e98dbb8c7083fcb4e8f53512e39256f Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Mon, 12 Sep 2022 18:53:53 +0200 Subject: [PATCH 02/11] Add two more inverters with their respective firmware versions --- tools/esp8266/User_Manual.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/esp8266/User_Manual.md b/tools/esp8266/User_Manual.md index 194c0b57..015bda24 100644 --- a/tools/esp8266/User_Manual.md +++ b/tools/esp8266/User_Manual.md @@ -237,6 +237,8 @@ Gather user inverter information here to understand what differs between some in | setje | HM-600 | | 1.0.08 | 2020 | 07-10 | 104 | | | | madmartin | HM-600 | 0.1.4 | 1.0.10 | 2021 | 11-01 | 104 | | | | lumapu | HM-1200 | 0.1.0 | 1.0.12 | 2020 | 06-24 | | | | +| chehrlic | HM-600 | | 1.0.10 | 2021 | 11-01 | 104 | | | +| chehrlic | TSOL-M800de | | 1.0.10 | 2021 | 11-01 | 104 | | | | | | | | | | | | | | | | | | | | | | | From de90c19eb3b5ac5a15453c78d1d4ee48d9bf25cc Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Mon, 12 Sep 2022 18:02:14 +0200 Subject: [PATCH 03/11] RPI: add handling for InverterDevInform_All message, handle RealTimeRunData_Reality similar to RealTimeRunData_Debug --- tools/rpi/hoymiles/__init__.py | 15 ++++----- tools/rpi/hoymiles/__main__.py | 44 ++++++++++++++++++++----- tools/rpi/hoymiles/decoders/__init__.py | 40 ++++++++++++++++++++++ 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/tools/rpi/hoymiles/__init__.py b/tools/rpi/hoymiles/__init__.py index d6ca70d2..965971b8 100644 --- a/tools/rpi/hoymiles/__init__.py +++ b/tools/rpi/hoymiles/__init__.py @@ -482,21 +482,20 @@ def compose_esb_packet(packet, mtu=17, **params): fragment = compose_esb_fragment(packet[i:i+mtu], **params) yield fragment -def compose_set_time_payload(timestamp=None): +def compose_send_time_payload(cmdId): """ Build set time request packet - :param timestamp: time to set (default: int(time.time()) ) - :type timestamp: int + :param cmd to request + :type cmd: uint8 :return: payload :rtype: bytes """ - if not timestamp: - timestamp = int(time.time()) + timestamp = int(time.time()) - payload = b'\x0b\x00' + payload = struct.pack('>B', cmdId) + b'\x00' payload = payload + struct.pack('>L', timestamp) # big-endian: msb at low address - payload = payload + b'\x00\x00\x00\x05\x00\x00\x00\x00' + payload = payload + b'\x00\x00\x00\x00\x00\x00\x00\x00' return frame_payload(payload) @@ -649,7 +648,7 @@ class InverterTransaction: except StopIteration: seq_last = max(frames, key=lambda frame:frame.seq).seq if len(frames) else 0 self.__retransmit_frame(seq_last + 1) - raise BufferError(f'Missing packet: Last packet {len(self.scratch)}') + raise BufferError(f'Missing packet: Last packet {seq_last + 1}') # Rebuild payload from unordered frames payload = b'' diff --git a/tools/rpi/hoymiles/__main__.py b/tools/rpi/hoymiles/__main__.py index 219b4449..7253b677 100644 --- a/tools/rpi/hoymiles/__main__.py +++ b/tools/rpi/hoymiles/__main__.py @@ -7,6 +7,7 @@ Hoymiles micro-inverters main application import sys import struct +from enum import IntEnum import re import time from datetime import datetime @@ -16,7 +17,7 @@ from yaml.loader import SafeLoader import paho.mqtt.client import hoymiles -def main_loop(): +def main_loop(do_init): """Main loop""" inverters = [ inverter for inverter in ahoy_config.get('inverters', []) @@ -25,9 +26,29 @@ def main_loop(): for inverter in inverters: if hoymiles.HOYMILES_DEBUG_LOGGING: print(f'Poll inverter {inverter["serial"]}') - poll_inverter(inverter) + poll_inverter(inverter, do_init) -def poll_inverter(inverter, retries=4): +class InfoCommands(IntEnum): + InverterDevInform_Simple = 0 # 0x00 + InverterDevInform_All = 1 # 0x01 + GridOnProFilePara = 2 # 0x02 + HardWareConfig = 3 # 0x03 + SimpleCalibrationPara = 4 # 0x04 + SystemConfigPara = 5 # 0x05 + RealTimeRunData_Debug = 11 # 0x0b + RealTimeRunData_Reality = 12 # 0x0c + RealTimeRunData_A_Phase = 13 # 0x0d + RealTimeRunData_B_Phase = 14 # 0x0e + RealTimeRunData_C_Phase = 15 # 0x0f + AlarmData = 17 # 0x11, Alarm data - all unsent alarms + AlarmUpdate = 18 # 0x12, Alarm data - all pending alarms + RecordData = 19 # 0x13 + InternalData = 20 # 0x14 + GetLossRate = 21 # 0x15 + GetSelfCheckState = 30 # 0x1e + InitDataState = 0xff + +def poll_inverter(inverter, do_init, retries=4): """ Send/Receive command_queue, initiate status poll on inverter @@ -39,11 +60,15 @@ def poll_inverter(inverter, retries=4): dtu_ser = ahoy_config.get('dtu', {}).get('serial') # Queue at least status data request - command_queue[str(inverter_ser)].append(hoymiles.compose_set_time_payload()) + inv_str = str(inverter_ser) + if do_init: + command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.InverterDevInform_All)) +# command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.SystemConfigPara)) + command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.RealTimeRunData_Debug)) - # Putt all queued commands for current inverter on air - while len(command_queue[str(inverter_ser)]) > 0: - payload = command_queue[str(inverter_ser)].pop(0) + # Put all queued commands for current inverter on air + while len(command_queue[inv_str]) > 0: + payload = command_queue[inv_str].pop(0) # Send payload {ttl}-times until we get at least one reponse payload_ttl = retries @@ -276,10 +301,13 @@ if __name__ == '__main__': loop_interval = ahoy_config.get('interval', 1) try: + do_init = True while True: t_loop_start = time.time() - main_loop() + main_loop(do_init) + + do_init = False print('', end='', flush=True) diff --git a/tools/rpi/hoymiles/decoders/__init__.py b/tools/rpi/hoymiles/decoders/__init__.py index 809dd796..4d8e218f 100644 --- a/tools/rpi/hoymiles/decoders/__init__.py +++ b/tools/rpi/hoymiles/decoders/__init__.py @@ -314,6 +314,28 @@ class EventsResponse(UnknownResponse): print(f' {fmt:7}: ' + str(struct.unpack('>' + fmt, chunk))) print(end='', flush=True) +class HardwareInfoResponse(UnknownResponse): + def __init__(self, *args, **params): + super().__init__(*args, **params) + """ + const byteAssign_t InfoAssignment[] = { + { FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1 }, + { FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1 }, + { FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1 }, + { FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1 } + }; + self.response = bytes('\x27\x1a\x07\xe5\x04\x4d\x03\x4a\x00\x68\x00\x00\x00\x00\xe6\xfb', 'latin1') + """ + fw_version, fw_build_yyyy, fw_build_mmdd, unknown, hw_id = struct.unpack('>HHHHH', self.response[0:10]) + + fw_version_maj = int((fw_version / 10000)) + fw_version_min = int((fw_version % 10000) / 100) + fw_version_pat = int((fw_version % 100)) + fw_build_mm = int(fw_build_mmdd / 100) + fw_build_dd = int(fw_build_mmdd % 100) + print() + print(f'Firmware: {fw_version_maj}.{fw_version_min}.{fw_version_pat} build at {fw_build_dd}/{fw_build_mm}/{fw_build_yyyy}, HW revision {hw_id}') + class DebugDecodeAny(UnknownResponse): """Default decoder""" @@ -359,6 +381,9 @@ class DebugDecodeAny(UnknownResponse): # 1121-Series Intervers, 1 MPPT, 1 Phase +class Hm300Decode01(HardwareInfoResponse): + """ Firmware version / date """ + class Hm300Decode02(EventsResponse): """ Inverter generic events log """ @@ -407,6 +432,9 @@ class Hm300Decode0B(StatusResponse): """ Inverter temperature in °C """ return self.unpack('>H', 26)[0]/10 +class Hm300Decode0C(Hm300Decode0B): + """ 1121-series mirco-inverters status data """ + class Hm300Decode11(EventsResponse): """ Inverter generic events log """ @@ -415,6 +443,9 @@ class Hm300Decode12(EventsResponse): # 1141-Series Inverters, 2 MPPT, 1 Phase +class Hm600Decode01(HardwareInfoResponse): + """ Firmware version / date """ + class Hm600Decode02(EventsResponse): """ Inverter generic events log """ @@ -492,6 +523,9 @@ class Hm600Decode0B(StatusResponse): """ Event counter """ return self.unpack('>H', 40)[0] +class Hm600Decode0C(Hm600Decode0B): + """ 1141-series mirco-inverters status data """ + class Hm600Decode11(EventsResponse): """ Inverter generic events log """ @@ -500,6 +534,9 @@ class Hm600Decode12(EventsResponse): # 1161-Series Inverters, 2 MPPT, 1 Phase +class Hm1200Decode01(HardwareInfoResponse): + """ Firmware version / date """ + class Hm1200Decode02(EventsResponse): """ Inverter generic events log """ @@ -619,6 +656,9 @@ class Hm1200Decode0B(StatusResponse): """ Event counter """ return self.unpack('>H', 60)[0] +class Hm1200Decode0C(Hm1200Decode0B): + """ 1161-series mirco-inverters status data """ + class Hm1200Decode11(EventsResponse): """ Inverter generic events log """ From d033b4a704870e27ed913d4d16ac45ed94b37b7b Mon Sep 17 00:00:00 2001 From: Kai Date: Mon, 12 Sep 2022 20:58:59 +0200 Subject: [PATCH 04/11] Make readme more clear --- tools/esp8266/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/esp8266/README.md b/tools/esp8266/README.md index f2fa10db..c2e6f570 100644 --- a/tools/esp8266/README.md +++ b/tools/esp8266/README.md @@ -26,6 +26,8 @@ This page describes how the module of a Wemos D1 mini and ESP8266 is wired to the radio module and is flashed with the latest Firmware.
Further information will help you to communicate to the compatible inverters. +You find the full [User_Manual here](https://github.com/grindylow/ahoy/blob/main/tools/esp8266/User_Manual.md) + ## Compatiblity For now the following Inverters should work out of the box: From 22b7fda09ba3d73da6e7511188d07a82c06a37b3 Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 12 Sep 2022 21:09:08 +0200 Subject: [PATCH 05/11] Update README.md relative link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2aa5839f..45066d88 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ List of approaches - [Others, C/C++](tools/nano/NRF24_SendRcv/) ## Quick Start with ESP8266 -- [Go here ✨](https://github.com/grindylow/ahoy/blob/main/tools/esp8266/README.md#things-needed) +- [Go here ✨](tools/esp8266/README.md#things-needed) ## Success Stories From edb8b0d746859a368dfa7737f28655c361339070 Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 12 Sep 2022 21:09:54 +0200 Subject: [PATCH 06/11] Update README.md relative link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2aa5839f..45066d88 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ List of approaches - [Others, C/C++](tools/nano/NRF24_SendRcv/) ## Quick Start with ESP8266 -- [Go here ✨](https://github.com/grindylow/ahoy/blob/main/tools/esp8266/README.md#things-needed) +- [Go here ✨](tools/esp8266/README.md#things-needed) ## Success Stories From 2365059f8265194215e80d7b35b0b9d66c7b20cc Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 12 Sep 2022 21:11:17 +0200 Subject: [PATCH 07/11] Update README.md relative links in md --- tools/esp8266/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/esp8266/README.md b/tools/esp8266/README.md index c2e6f570..e2b784e8 100644 --- a/tools/esp8266/README.md +++ b/tools/esp8266/README.md @@ -26,7 +26,7 @@ This page describes how the module of a Wemos D1 mini and ESP8266 is wired to the radio module and is flashed with the latest Firmware.
Further information will help you to communicate to the compatible inverters. -You find the full [User_Manual here](https://github.com/grindylow/ahoy/blob/main/tools/esp8266/User_Manual.md) +You find the full [User_Manual here](tools/esp8266/User_Manual.md) ## Compatiblity For now the following Inverters should work out of the box: @@ -58,7 +58,7 @@ Any other ESP8266 Board with at least 4MBytes of ROM could work as well, dependi #### There are fake NRF24L01+ Modules out there Whatch out, there are some fake NRF24L01+ Modules out there that seem to use rebranded NRF24L01 Chips (without the +).
-An example can be found in [Issue #230](https://github.com/grindylow/ahoy/issues/230).
+An example can be found in [Issue #230](https://github.com/lumapu/ahoy/issues/230).
You are welcome to add more examples of faked chips. We will that information here.
## Wiring things up @@ -201,7 +201,7 @@ When everything is wired up and the firmware is flashed, it is time to connect t ## MQTT command to set the DTU without webinterface - [Read here](https://github.com/grindylow/ahoy/blob/main/tools/esp8266/User_Manual.md) + [Read here](tools/esp8266/User_Manual.md) @@ -226,4 +226,4 @@ https://discord.gg/WzhxEY62mB ## ToDo -[See this post](https://github.com/grindylow/ahoy/issues/142) +[See this post](https://github.com/lumapu/ahoy/issues/142) From d8409157948f4c27a3f4c0a13d97dcedc7e53c5d Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 12 Sep 2022 21:12:26 +0200 Subject: [PATCH 08/11] Update README.md fix link --- tools/esp8266/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/esp8266/README.md b/tools/esp8266/README.md index e2b784e8..57ee5c32 100644 --- a/tools/esp8266/README.md +++ b/tools/esp8266/README.md @@ -26,7 +26,7 @@ This page describes how the module of a Wemos D1 mini and ESP8266 is wired to the radio module and is flashed with the latest Firmware.
Further information will help you to communicate to the compatible inverters. -You find the full [User_Manual here](tools/esp8266/User_Manual.md) +You find the full [User_Manual here](User_Manual.md) ## Compatiblity For now the following Inverters should work out of the box: From 89503e6bb08f52f4a42a246179fdd69f007ae2b8 Mon Sep 17 00:00:00 2001 From: Kai Date: Mon, 12 Sep 2022 21:14:08 +0200 Subject: [PATCH 09/11] Update README.md --- tools/esp8266/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/esp8266/README.md b/tools/esp8266/README.md index e2b784e8..57ee5c32 100644 --- a/tools/esp8266/README.md +++ b/tools/esp8266/README.md @@ -26,7 +26,7 @@ This page describes how the module of a Wemos D1 mini and ESP8266 is wired to the radio module and is flashed with the latest Firmware.
Further information will help you to communicate to the compatible inverters. -You find the full [User_Manual here](tools/esp8266/User_Manual.md) +You find the full [User_Manual here](User_Manual.md) ## Compatiblity For now the following Inverters should work out of the box: From 03893e52bab307673f7260e7d89ecd8960e8029e Mon Sep 17 00:00:00 2001 From: Kai Mosebach <2472195+FreshXOpenSource@users.noreply.github.com> Date: Tue, 13 Sep 2022 10:51:23 +0200 Subject: [PATCH 10/11] add homeassistant examples --- tools/homeassistant/README.md | 9 +++++++++ tools/homeassistant/autodiscovery.yaml | 4 ++++ tools/homeassistant/manual.yaml | 23 +++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 tools/homeassistant/README.md create mode 100644 tools/homeassistant/autodiscovery.yaml create mode 100644 tools/homeassistant/manual.yaml diff --git a/tools/homeassistant/README.md b/tools/homeassistant/README.md new file mode 100644 index 00000000..0cdd9d84 --- /dev/null +++ b/tools/homeassistant/README.md @@ -0,0 +1,9 @@ +# HomeAssistant Examples + +Disclaimer: these are collected examples from https://www.mikrocontroller.net/topic/525778 (Page 12) + +in manual.yaml you will find the setup for manual configuration, adapt your name (Terrasse) and the topic (inverter) to your needs and place it into configuration.yaml + +in autodiscovery.yaml you will find the setup for automatic discovery of the inverter + +Note: the config might need adaption to your system (mqtt, homeassistant etc) diff --git a/tools/homeassistant/autodiscovery.yaml b/tools/homeassistant/autodiscovery.yaml new file mode 100644 index 00000000..48e29163 --- /dev/null +++ b/tools/homeassistant/autodiscovery.yaml @@ -0,0 +1,4 @@ +mqtt: + broker: http:// + discovery: true + discovery_prefix: inverter diff --git a/tools/homeassistant/manual.yaml b/tools/homeassistant/manual.yaml new file mode 100644 index 00000000..793a30e5 --- /dev/null +++ b/tools/homeassistant/manual.yaml @@ -0,0 +1,23 @@ +sensor: + - platform: mqtt + state_topic: "inverter/Terrasse/ch0/P_AC" + name: "Aktuelle Produktion HM-600" + device_class: energy + unit_of_measurement: "Watt" + value_template: > + {{value|round(2)}} + state_class: total_increasing + unique_id: "current_hm600" + last_reset_topic: "inverter/Terrasse/ch0/P_AC" + last_reset_value_template: "1970-01-01T00:00:00+00:00" + - platform: mqtt + state_topic: "inverter/Terrasse/ch0/YieldTotal" + name: "Gesamtproduktion HM-600" + device_class: energy + unit_of_measurement: "KW/H" + value_template: > + {{value|round(2)}} + state_class: total_increasing + unique_id: "total_hm600" + last_reset_topic: "inverter/Terrasse/ch0/YieldTotal" + last_reset_value_template: "1970-01-01T00:00:00+00:00" From fb6250a1098f371d96fe409849e011ce1b970f19 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Mon, 12 Sep 2022 19:45:20 +0200 Subject: [PATCH 11/11] RPI: Request alarm when alarm id changes --- tools/rpi/hoymiles/__init__.py | 12 ++++++++---- tools/rpi/hoymiles/__main__.py | 10 +++++++++- tools/rpi/hoymiles/decoders/__init__.py | 6 ++++-- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/tools/rpi/hoymiles/__init__.py b/tools/rpi/hoymiles/__init__.py index 965971b8..5b305e70 100644 --- a/tools/rpi/hoymiles/__init__.py +++ b/tools/rpi/hoymiles/__init__.py @@ -482,7 +482,7 @@ def compose_esb_packet(packet, mtu=17, **params): fragment = compose_esb_fragment(packet[i:i+mtu], **params) yield fragment -def compose_send_time_payload(cmdId): +def compose_send_time_payload(cmdId, alarm_id=0): """ Build set time request packet @@ -493,9 +493,13 @@ def compose_send_time_payload(cmdId): """ timestamp = int(time.time()) - payload = struct.pack('>B', cmdId) + b'\x00' - payload = payload + struct.pack('>L', timestamp) # big-endian: msb at low address - payload = payload + b'\x00\x00\x00\x00\x00\x00\x00\x00' + # indices from esp8266 hmRadio.h / sendTimePacket() + payload = struct.pack('>B', cmdId) # 10 + payload = payload + b'\x00' # 11 + payload = payload + struct.pack('>L', timestamp) # 12..15 big-endian: msb at low address + payload = payload + b'\x00\x00' # 16..17 + payload = payload + struct.pack('>H', alarm_id) # 18..19 + payload = payload + b'\x00\x00\x00\x00' # 20..23 return frame_payload(payload) diff --git a/tools/rpi/hoymiles/__main__.py b/tools/rpi/hoymiles/__main__.py index 7253b677..4fbaa8ac 100644 --- a/tools/rpi/hoymiles/__main__.py +++ b/tools/rpi/hoymiles/__main__.py @@ -120,6 +120,11 @@ def poll_inverter(inverter, do_init, retries=4): string_id = string_id + 1 print() + if 'event_count' in data: + if 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])) + if mqtt_client: mqtt_send_status(mqtt_client, inverter_ser, data, topic=inverter.get('mqtt', {}).get('topic', None)) @@ -244,6 +249,7 @@ if __name__ == '__main__': mqtt_client = None + event_message_index = {} command_queue = {} mqtt_command_topic_subs = [] @@ -286,7 +292,9 @@ if __name__ == '__main__': g_inverters = [g_inverter.get('serial') for g_inverter in ahoy_config.get('inverters', [])] for g_inverter in ahoy_config.get('inverters', []): g_inverter_ser = g_inverter.get('serial') - command_queue[str(g_inverter_ser)] = [] + inv_str = str(g_inverter_ser) + command_queue[inv_str] = [] + event_message_index[inv_str] = 0 # # Enables and subscribe inverter to mqtt /command-Topic diff --git a/tools/rpi/hoymiles/decoders/__init__.py b/tools/rpi/hoymiles/decoders/__init__.py index 4d8e218f..42d1fd0f 100644 --- a/tools/rpi/hoymiles/decoders/__init__.py +++ b/tools/rpi/hoymiles/decoders/__init__.py @@ -294,10 +294,12 @@ class EventsResponse(UnknownResponse): crc_valid = self.validate_crc_m() if crc_valid: - print(' payload has valid modbus crc') + #print(' payload has valid modbus crc') self.response = self.response[:-2] - status = self.response[:2] + status = struct.unpack('>H', self.response[:2])[0] + a_text = self.alarm_codes.get(status, 'N/A') + print (f' Inverter status: {a_text} ({status})') chunk_size = 12 for i_chunk in range(2, len(self.response), chunk_size):