diff --git a/custom_components/sigen/manifest.json b/custom_components/sigen/manifest.json index 423bb58..d99f3bf 100644 --- a/custom_components/sigen/manifest.json +++ b/custom_components/sigen/manifest.json @@ -21,5 +21,5 @@ "loggers": ["custom_components.sigen"], "quality_scale": "custom", "requirements": ["pymodbus>=3.0.0"], - "version": "1.0.3" + "version": "1.0.4" } \ No newline at end of file diff --git a/custom_components/sigen/modbus.py b/custom_components/sigen/modbus.py index 4c981e4..61284a4 100644 --- a/custom_components/sigen/modbus.py +++ b/custom_components/sigen/modbus.py @@ -567,38 +567,41 @@ async def async_write_registers( async with self._locks[key]: if register_type in [RegisterType.HOLDING, RegisterType.WRITE_ONLY]: - # For Modbus addresses starting with 4xxxx, - # some devices expect the offset (address - 40001) - if address >= 40001: - # Try with the offset addressing first - offset_address = address - 40001 - _LOGGER.debug( - "Writing to registers starting at %s (offset address %s) \ - with values %s for slave %s", - address, offset_address, values, slave_id - ) - result = await client.write_registers( - address=offset_address, values=values, slave=slave_id - ) - else: - _LOGGER.debug( - "Writing to registers starting at %s with values %s for slave %s", - address, values, slave_id - ) - result = await client.write_registers( - address=address, values=values, slave=slave_id - ) - + tried_offset = False + last_error = None + # Try offset addressing first if address >= 40001 + # if address >= 40001: + # offset_address = address - 40001 + # _LOGGER.debug( + # "Trying write_registers at offset address %s (orig %s) with values %s for slave %s", + # offset_address, address, values, slave_id + # ) + # result = await client.write_registers( + # address=offset_address, values=values, slave=slave_id + # ) + # if not result.isError(): + # _LOGGER.debug("Successfully wrote to registers at offset address %s", offset_address) + # return + # _LOGGER.debug("Offset write failed, will try original address. Error: %s", result) + # last_error = result + # tried_offset = True + # Try original address if not tried yet or offset failed + _LOGGER.debug( + "Trying write_registers at original address %s with values %s for slave %s", + address, values, slave_id + ) + result = await client.write_registers( + address=address, values=values, slave=slave_id + ) if result.isError(): _LOGGER.warning("Modbus write_registers error for %s:%s@%s (address %s): %s. Marking connection as closed.", key[0], key[1], slave_id, address, result) self._connected[key] = False _LOGGER.debug("Error response from write_registers: %s", result) raise SigenergyModbusError( - f"Error writing registers at address {address}: {result}" + f"Error writing registers at address {address}: {result if not tried_offset else last_error}, {result}" ) else: - _LOGGER.debug("Successfully wrote to registers starting at address %s", - address) + _LOGGER.debug("Successfully wrote to registers at address %s", address) else: raise SigenergyModbusError( f"Register type {register_type} is not writable" @@ -922,7 +925,8 @@ async def async_write_parameter( ValueError: If device_type is invalid or device_identifier is missing when required. """ if self.read_only: - raise SigenergyModbusError("Cannot write parameter while in read-only mode") + _LOGGER.error("Cannot write parameter while in read-only mode") + return slave_id: Optional[int] = None parameter_registers: Dict[str, ModbusRegisterDefinition] = {} @@ -1005,104 +1009,104 @@ async def async_write_parameter( # plant_info is guaranteed to be a dict here # Special handling for plant_remote_ems_enable register - if register_name == "plant_remote_ems_enable": - _LOGGER.debug("Special handling for plant_remote_ems_enable register") - approaches = [ - {"function": "write_registers", "address": register_def.address, - "values": [int(value)]}, - {"function": "write_register", "address": register_def.address, - "value": int(value)}, - {"function": "write_registers", "address": register_def.address - 40001, - "values": [int(value)]}, - {"function": "write_register", "address": register_def.address - 40001, - "value": int(value)}, - ] - last_error = None - success = False - try: - async with self._locks[key]: - client = await self._get_client(device_info) - for approach in approaches: - try: - _LOGGER.debug( - "Plant write attempt: %s", approach['description'] \ - if 'description' in approach \ - else f"{approach['function']} @ {approach['address']}") - if approach["function"] == "write_registers": - result = await client.write_registers( - address=approach["address"], - values=approach["values"], - slave=slave_id - ) - else: - result = await client.write_register( - address=approach["address"], - value=approach["value"], - slave=slave_id - ) - if not result.isError(): - success = True - break - last_error = result - except Exception as ex_inner: - last_error = ex_inner - if not success: - raise SigenergyModbusError("Failed plant_remote_ems_enable write after " - f"all attempts. Last error: {last_error}") - return # Success - except (ConnectionException, ModbusException, SigenergyModbusError) as ex_outer: - self._connected[key] = False - raise SigenergyModbusError(f"Error during special plant write: {ex_outer}") \ - from ex_outer - except Exception as ex_outer: - raise SigenergyModbusError("Unexpected error during special plant write:" - f" {ex_outer}") from ex_outer + # if register_name == "plant_remote_ems_enable": + # _LOGGER.debug("Special handling for plant_remote_ems_enable register") + # approaches = [ + # {"function": "write_registers", "address": register_def.address, + # "values": [int(value)]}, + # {"function": "write_register", "address": register_def.address, + # "value": int(value)}, + # {"function": "write_registers", "address": register_def.address - 40001, + # "values": [int(value)]}, + # {"function": "write_register", "address": register_def.address - 40001, + # "value": int(value)}, + # ] + # last_error = None + # success = False + # try: + # async with self._locks[key]: + # client = await self._get_client(device_info) + # for approach in approaches: + # try: + # _LOGGER.debug( + # "Plant write attempt: %s", approach['description'] \ + # if 'description' in approach \ + # else f"{approach['function']} @ {approach['address']}") + # if approach["function"] == "write_registers": + # result = await client.write_registers( + # address=approach["address"], + # values=approach["values"], + # slave=slave_id + # ) + # else: + # result = await client.write_register( + # address=approach["address"], + # value=approach["value"], + # slave=slave_id + # ) + # if not result.isError(): + # success = True + # break + # last_error = result + # except Exception as ex_inner: + # last_error = ex_inner + # if not success: + # raise SigenergyModbusError("Failed plant_remote_ems_enable write after " + # f"all attempts. Last error: {last_error}") + # return # Success + # except (ConnectionException, ModbusException, SigenergyModbusError) as ex_outer: + # self._connected[key] = False + # raise SigenergyModbusError(f"Error during special plant write: {ex_outer}") \ + # from ex_outer + # except Exception as ex_outer: + # raise SigenergyModbusError("Unexpected error during special plant write:" + # f" {ex_outer}") from ex_outer # Special handling for U32/S32 registers - elif register_def.data_type in [DataType.U32, DataType.S32]: - _LOGGER.debug("Special handling for U32/S32 register %s", register_name) - encoded_values = self._encode_value(value=value, data_type=register_def.data_type, - gain=register_def.gain) - _LOGGER.debug("Encoded values for %s: %s", register_name, encoded_values) - approaches = [ - {"address": register_def.address}, - {"address": register_def.address - 40001}, - {"address": register_def.address - 40000}, - {"address": register_def.address % 10000}, - ] - last_error = None - success = False - try: - async with self._locks[key]: - client = await self._get_client(device_info) - for approach in approaches: - try: - _LOGGER.debug("Plant U32/S32 write attempt: write_registers @ %s", - approach['address']) - result = await client.write_registers( - address=approach["address"], - values=encoded_values, - slave=slave_id - ) - if not result.isError(): - success = True - break - last_error = result - except Exception as ex_inner: - last_error = ex_inner - if not success: - raise SigenergyModbusError( - f"Failed U32/S32 write for {register_name} after all attempts. " - f"Last error: {last_error}" - ) - return # Success - except (ConnectionException, ModbusException, SigenergyModbusError) as ex_outer: - self._connected[key] = False - raise SigenergyModbusError("Error during special plant U32/S32 write:"\ - f" {ex_outer}") from ex_outer - except Exception as ex_outer: - raise SigenergyModbusError("Unexpected error during special plant " \ - f"U32/S32 write: {ex_outer}") from ex_outer + # if register_def.data_type in [DataType.U32, DataType.S32]: + # _LOGGER.debug("Special handling for U32/S32 register %s", register_name) + # encoded_values = self._encode_value(value=value, data_type=register_def.data_type, + # gain=register_def.gain) + # _LOGGER.debug("Encoded values for %s: %s", register_name, encoded_values) + # approaches = [ + # {"address": register_def.address}, + # {"address": register_def.address - 40001}, + # {"address": register_def.address - 40000}, + # {"address": register_def.address % 10000}, + # ] + # last_error = None + # success = False + # try: + # async with self._locks[key]: + # client = await self._get_client(device_info) + # for approach in approaches: + # try: + # _LOGGER.debug("Plant U32/S32 write attempt: write_registers @ %s", + # approach['address']) + # result = await client.write_registers( + # address=approach["address"], + # values=encoded_values, + # slave=slave_id + # ) + # if not result.isError(): + # success = True + # break + # last_error = result + # except Exception as ex_inner: + # last_error = ex_inner + # if not success: + # raise SigenergyModbusError( + # f"Failed U32/S32 write for {register_name} after all attempts. " + # f"Last error: {last_error}" + # ) + # return # Success + # except (ConnectionException, ModbusException, SigenergyModbusError) as ex_outer: + # self._connected[key] = False + # raise SigenergyModbusError("Error during special plant U32/S32 write:"\ + # f" {ex_outer}") from ex_outer + # except Exception as ex_outer: + # raise SigenergyModbusError("Unexpected error during special plant " \ + # f"U32/S32 write: {ex_outer}") from ex_outer # === General Write Logic === # (Executes if device_type is not 'plant' or if it's a plant register