git: 2265bb7f1054 - main - www/domoticz*: Fix build for boost >= 1.87

From: Xavier Beaudouin <kiwi_at_FreeBSD.org>
Date: Fri, 16 May 2025 14:02:43 UTC
The branch main has been updated by kiwi:

URL: https://cgit.FreeBSD.org/ports/commit/?id=2265bb7f1054b01eafc34b3619be028cd222a402

commit 2265bb7f1054b01eafc34b3619be028cd222a402
Author:     erwin freebsd <erwin.freebsd-bugzilla@bk3.nl>
AuthorDate: 2025-05-16 11:18:15 +0000
Commit:     Xavier Beaudouin <kiwi@FreeBSD.org>
CommitDate: 2025-05-16 14:02:12 +0000

    www/domoticz*: Fix build for boost >= 1.87
    
    With boost >= 1.87, domoticz cannot be built. This has been
    handled by PR 6252 on domoticz github.
    
    I'm adding the patches to files/ because they do not apply
    cleanly unfortunately when managed via PATCH_SITES.
    The problem is that the upstream patch modifies the
    msbuild-related parts of the build system. Those parts are
    not relevant on FreeBSD and has been already removed in
    previous updates. So adding modified version of the patches
    from the GitHub PR to files/ seems to be the right approach.
    
    PR:     286076
    Reported by:    <erwin.freebsd-bugzilla@bk3.nl>
    Approved by:    0mp (mentor)
    Differential Revision:  https://reviews.freebsd.org/D50373
---
 www/domoticz-devel/Makefile           |    2 -
 www/domoticz-devel/files/patch-pr6252 | 2178 +++++++++++++++++++++++++++++++++
 www/domoticz/Makefile                 |    3 +-
 www/domoticz/files/patch-pr6252       | 2178 +++++++++++++++++++++++++++++++++
 4 files changed, 4357 insertions(+), 4 deletions(-)

diff --git a/www/domoticz-devel/Makefile b/www/domoticz-devel/Makefile
index e8954e448702..7f56841b2567 100644
--- a/www/domoticz-devel/Makefile
+++ b/www/domoticz-devel/Makefile
@@ -11,8 +11,6 @@ WWW=		https://www.domoticz.com
 LICENSE=	GPLv3
 LICENSE_FILE=	${WRKSRC}/License.txt
 
-BROKEN=		fails to build with Boost>=1.87
-
 LIB_DEPENDS=	libcurl.so:ftp/curl \
 		libboost_system.so:devel/boost-libs \
 		libjsoncpp.so:devel/jsoncpp \
diff --git a/www/domoticz-devel/files/patch-pr6252 b/www/domoticz-devel/files/patch-pr6252
new file mode 100644
index 000000000000..34c4efd3f4e4
--- /dev/null
+++ b/www/domoticz-devel/files/patch-pr6252
@@ -0,0 +1,2178 @@
+Fix for boost 1.87
+
+Removed the diff for the msbuild project files, didn't apply cleanly and not relevant anyway.
+This will merged in the next stable of domoticz.
+
+Pullrequest: https://github.com/domoticz/domoticz/pull/6252
+Patch: https://patch-diff.githubusercontent.com/raw/domoticz/domoticz/pull/6252.patch-diff
+
+diff --git hardware/ASyncSerial.cpp hardware/ASyncSerial.cpp
+index 52c950d..6147cdb 100644
+--- hardware/ASyncSerial.cpp
++++ hardware/ASyncSerial.cpp
+@@ -54,7 +54,7 @@ public:
+   {
+   }
+ 
+-    boost::asio::io_service io; ///< Io service object
++    boost::asio::io_context io; ///< Io service object
+     boost::asio::serial_port port; ///< Serial port object
+     boost::thread backgroundThread; ///< Thread that runs read/write operations
+     bool open{ false };		    ///< True if port open
+@@ -117,10 +117,10 @@ void AsyncSerial::open(const std::string& devname, unsigned int baud_rate,
+ 		throw;
+ 	}
+ 
+-	pimpl->io.reset();
++	pimpl->io.restart();
+ 
+-	// This gives some work to the io_service before it is started
+-	pimpl->io.post([this] { return doRead(); });
++	// This gives some work to the io_context before it is started
++	boost::asio::post(pimpl->io, [this] { return doRead(); });
+ 
+ 	boost::thread t([p = &pimpl->io] { p->run(); });
+ 	pimpl->backgroundThread.swap(t);
+@@ -149,10 +149,10 @@ void AsyncSerial::openOnlyBaud(const std::string& devname, unsigned int baud_rat
+ 		throw;
+ 	}
+ 
+-	pimpl->io.reset();
++	pimpl->io.restart();
+ 
+-	//This gives some work to the io_service before it is started
+-	pimpl->io.post([this] { return doRead(); });
++	//This gives some work to the io_context before it is started
++	boost::asio::post(pimpl->io, [this] { return doRead(); });
+ 
+ 	boost::thread t([p = &pimpl->io] { p->run(); });
+ 	pimpl->backgroundThread.swap(t);
+@@ -176,9 +176,9 @@ void AsyncSerial::close()
+     if(!isOpen()) return;
+ 
+     pimpl->open = false;
+-    pimpl->io.post([this] { doClose(); });
++    boost::asio::post(pimpl->io, [this] { doClose(); });
+     pimpl->backgroundThread.join();
+-    pimpl->io.reset();
++    pimpl->io.restart();
+     if(errorStatus())
+     {
+         throw(boost::system::system_error(boost::system::error_code(),
+@@ -192,7 +192,7 @@ void AsyncSerial::write(const char *data, size_t size)
+         std::lock_guard<std::mutex> l(pimpl->writeQueueMutex);
+         pimpl->writeQueue.insert(pimpl->writeQueue.end(),data,data+size);
+     }
+-    pimpl->io.post([this] { doWrite(); });
++    boost::asio::post(pimpl->io, [this] { doWrite(); });
+ }
+ 
+ void AsyncSerial::write(const std::string &data)
+@@ -201,7 +201,7 @@ void AsyncSerial::write(const std::string &data)
+ 		std::lock_guard<std::mutex> l(pimpl->writeQueueMutex);
+ 		pimpl->writeQueue.insert(pimpl->writeQueue.end(), data.c_str(), data.c_str()+data.size());
+ 	}
+-	pimpl->io.post([this] { doWrite(); });
++	boost::asio::post(pimpl->io, [this] { doWrite(); });
+ }
+ 
+ void AsyncSerial::write(const std::vector<char>& data)
+@@ -211,7 +211,7 @@ void AsyncSerial::write(const std::vector<char>& data)
+         pimpl->writeQueue.insert(pimpl->writeQueue.end(),data.begin(),
+                 data.end());
+     }
+-    pimpl->io.post([this] { doWrite(); });
++    boost::asio::post(pimpl->io, [this] { doWrite(); });
+ }
+ 
+ void AsyncSerial::writeString(const std::string& s)
+@@ -220,7 +220,7 @@ void AsyncSerial::writeString(const std::string& s)
+         std::lock_guard<std::mutex> l(pimpl->writeQueueMutex);
+         pimpl->writeQueue.insert(pimpl->writeQueue.end(),s.begin(),s.end());
+     }
+-    pimpl->io.post([this] { doWrite(); });
++    boost::asio::post(pimpl->io, [this] { doWrite(); });
+ }
+ 
+ void AsyncSerial::doRead()
+diff --git hardware/ASyncSerial.h hardware/ASyncSerial.h
+index 0a51ef0..de83f8a 100644
+--- hardware/ASyncSerial.h
++++ hardware/ASyncSerial.h
+@@ -123,27 +123,27 @@ class AsyncSerial : private domoticz::noncopyable
+ 
+ 	/**
+ 	 * Callback called to start an asynchronous read operation.
+-	 * This callback is called by the io_service in the spawned thread.
++	 * This callback is called by the io_context in the spawned thread.
+ 	 */
+ 	void doRead();
+ 
+ 	/**
+ 	 * Callback called at the end of the asynchronous operation.
+-	 * This callback is called by the io_service in the spawned thread.
++	 * This callback is called by the io_context in the spawned thread.
+ 	 */
+ 	void readEnd(const boost::system::error_code &error, size_t bytes_transferred);
+ 
+ 	/**
+ 	 * Callback called to start an asynchronous write operation.
+ 	 * If it is already in progress, does nothing.
+-	 * This callback is called by the io_service in the spawned thread.
++	 * This callback is called by the io_context in the spawned thread.
+ 	 */
+ 	void doWrite();
+ 
+ 	/**
+ 	 * Callback called at the end of an asynchronuous write operation,
+ 	 * if there is more data to write, restarts a new write operation.
+-	 * This callback is called by the io_service in the spawned thread.
++	 * This callback is called by the io_context in the spawned thread.
+ 	 */
+ 	void writeEnd(const boost::system::error_code &error);
+ 
+diff --git hardware/ASyncTCP.cpp hardware/ASyncTCP.cpp
+index a375561..7c3b536 100644
+--- hardware/ASyncTCP.cpp
++++ hardware/ASyncTCP.cpp
+@@ -4,213 +4,241 @@
+ #include <boost/system/error_code.hpp>     // for error_code
+ #include "../main/Logger.h"
+ 
+-struct hostent;
+-
+ #define MAX_TCP_BUFFER_SIZE 4096
+ 
+-#ifndef WIN32
+-	#include <unistd.h> //gethostbyname
+-#endif
+-
+ #define STATUS_OK(err) !err
+-
+-ASyncTCP::ASyncTCP(const bool secure)
++#define STATUS_ERR(err) err
++
++ASyncTCP::ASyncTCP(const bool secure) :
++	m_Tcpwork(boost::asio::make_work_guard(m_io_context))
++	, m_Socket(m_io_context)
++	, m_Resolver(m_io_context)
++	, m_ReconnectTimer(m_io_context)
++	, m_TimeoutTimer(m_io_context)
++	, m_SendStrand(m_io_context)
+ #ifdef WWW_ENABLE_SSL
+-	: mSecure(secure)
++	, m_bSecure(secure)
+ #endif
+ {
+ 	m_pRXBuffer = new uint8_t[MAX_TCP_BUFFER_SIZE];
+ #ifdef WWW_ENABLE_SSL
+ 	mContext.set_verify_mode(boost::asio::ssl::verify_none);
+-	if (mSecure) 
++	if (m_bSecure)
+ 	{
+-		mSslSocket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(mIos, mContext));
++		m_SslSocket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_context, mContext));
+ 	}
+ #endif
+ }
+ 
+ ASyncTCP::~ASyncTCP()
+ {
+-	assert(mTcpthread == nullptr);
+-	mIsTerminating = true;
+-	if (mTcpthread)
++	assert(m_Tcpthread == nullptr);
++	m_bIsTerminating = true;
++	if (m_Tcpthread)
+ 	{
+ 		//This should never happen. terminate() never called!!
+-		_log.Log(LOG_ERROR, "ASyncTCP: Workerthread not closed. terminate() never called!!!");
+-		mIos.stop();
+-		if (mTcpthread)
++		_log.Log(LOG_ERROR, "ASyncTCP: Worker thread not closed. terminate() never called!!!");
++		m_io_context.stop();
++		if (m_Tcpthread)
+ 		{
+-			mTcpthread->join();
+-			mTcpthread.reset();
++			m_Tcpthread->join();
++			m_Tcpthread.reset();
+ 		}
+ 	}
+ 	if (m_pRXBuffer != nullptr)
+ 		delete[] m_pRXBuffer;
+ }
+ 
+-void ASyncTCP::SetReconnectDelay(int32_t Delay)
++void ASyncTCP::SetReconnectDelay(const int32_t Delay)
+ {
+-	mReconnectDelay = Delay;
++	m_iReconnectDelay = Delay;
+ }
+ 
+ void ASyncTCP::connect(const std::string& ip, uint16_t port)
+ {
+-	assert(!mSocket.is_open());
+-	if (mSocket.is_open())
++	assert(!m_Socket.is_open());
++	if (m_Socket.is_open())
+ 	{
+ 		_log.Log(LOG_ERROR, "ASyncTCP: connect called while socket is still open. !!!");
+ 		terminate();
+ 	}
+ 
+-	// RK: We reset mIos here because it might have been stopped in terminate()
+-	mIos.reset();
+-	// RK: After the reset, we need to provide it work anew
+-	mTcpwork = std::make_shared<boost::asio::io_service::work>(mIos);
+-	if (!mTcpthread)
+-		mTcpthread = std::make_shared<std::thread>([p = &mIos] { p->run(); });
+-
+-	mIp = ip;
+-	mPort = port;
++	m_IP = ip;
++	m_Port = port;
+ 	std::string port_str = std::to_string(port);
+-	boost::asio::ip::tcp::resolver::query query(ip, port_str);
+ 	timeout_start_timer();
+-	mResolver.async_resolve(query, [this](auto &&err, auto &&iter) { cb_resolve_done(err, iter); });
++
++	m_Resolver.async_resolve(
++		ip, port_str,
++		[this](const boost::system::error_code& error, const boost::asio::ip::tcp::resolver::results_type& endpoints) {
++			handle_resolve(error, endpoints);
++		}
++	);
++
++	// RK: We restart m_io_context here because it might have been stopped in terminate()
++	m_io_context.restart();
++	// RK: After the reset, we need to provide it work anew
++	m_Tcpwork.reset();
++	m_Tcpwork.emplace(boost::asio::make_work_guard(m_io_context));
++	if (!m_Tcpthread)
++		m_Tcpthread = std::make_shared<std::thread>([p = &m_io_context] { p->run(); });
+ }
+ 
+-void ASyncTCP::cb_resolve_done(const boost::system::error_code& error, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
++void ASyncTCP::handle_resolve(const boost::system::error_code& error, const boost::asio::ip::tcp::resolver::results_type &endpoints)
+ {
+-	if (mIsTerminating) return;
++	if (m_bIsTerminating) return;
+ 
+-	if (STATUS_OK(error))
+-	{
+-		connect_start(endpoint_iterator);
+-	}
+-	else
++	if (STATUS_ERR(error))
+ 	{
+ 		process_error(error);
++		return;
+ 	}
+-}
+-
+-void ASyncTCP::connect_start(boost::asio::ip::tcp::resolver::iterator& endpoint_iterator)
+-{
+-	if (mIsConnected) return;
+-
+-	mEndPoint = *endpoint_iterator++;
++	if (m_bIsConnected) return;
+ 
+ 	timeout_start_timer();
++
+ #ifdef WWW_ENABLE_SSL
+-	if (mSecure)
++	if (m_bSecure)
+ 	{
+ 		// we reset the ssl socket, because the ssl context needs to be reinitialized after a reconnect
+-		mSslSocket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(mIos, mContext));
+-		mSslSocket->lowest_layer().async_connect(mEndPoint, [this, endpoint_iterator](auto &&err) mutable { cb_connect_done(err, endpoint_iterator); });
++		m_SslSocket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_context, mContext));
++		boost::asio::async_connect(m_SslSocket->lowest_layer(), endpoints,
++			[this](const boost::system::error_code& error, const boost::asio::ip::tcp::endpoint& endpoint)
++			{
++				handle_connect(error, endpoint);
++			}
++		);
+ 	}
+ 	else
+ #endif
+ 	{
+-		mSocket.async_connect(mEndPoint, [this, endpoint_iterator](auto &&err) mutable { cb_connect_done(err, endpoint_iterator); });
++		boost::asio::async_connect(m_Socket, endpoints,
++			[this](const boost::system::error_code& error, const boost::asio::ip::tcp::endpoint& endpoint)
++			{
++				handle_connect(error, endpoint);
++			}
++		);
+ 	}
+ }
+ 
+-void ASyncTCP::cb_connect_done(const boost::system::error_code& error, boost::asio::ip::tcp::resolver::iterator &endpoint_iterator)
++void ASyncTCP::handle_connect(const boost::system::error_code& error, const boost::asio::ip::tcp::endpoint& /*endpoint*/)
+ {
+-	if (mIsTerminating) return;
++	if (m_bIsTerminating) return;
+ 
+-	if (STATUS_OK(error))
++	if (STATUS_ERR(error))
+ 	{
++		process_error(error);
++		return;
++	}
+ #ifdef WWW_ENABLE_SSL
+-		if (mSecure) 
+-		{
+-			timeout_start_timer();
+-			mSslSocket->async_handshake(boost::asio::ssl::stream_base::client, [this](auto &&err) { cb_handshake_done(err); });
+-		}
+-		else
+-#endif
+-		{
+-			process_connection();
+-		}
++	if (m_bSecure)
++	{
++		timeout_start_timer();
++		m_SslSocket->async_handshake(boost::asio::ssl::stream_base::client,
++			[this](const boost::system::error_code& error) {
++				cb_handshake_done(error);
++			}
++		);
+ 	}
+-	else 
++	else
++#endif
+ 	{
+-		if (endpoint_iterator != boost::asio::ip::tcp::resolver::iterator()) 
+-		{
+-			// The connection failed. Try the next endpoint in the list.
+-			connect_start(endpoint_iterator);
+-			return;
+-		}
+-		process_error(error);
++		process_connection();
+ 	}
+ }
+ 
+ #ifdef WWW_ENABLE_SSL
+ void ASyncTCP::cb_handshake_done(const boost::system::error_code& error)
+ {
+-	if (mIsTerminating) return;
++	if (m_bIsTerminating) return;
+ 
+-	if (STATUS_OK(error))
+-	{
+-		process_connection();
+-	}
+-	else
++	if (STATUS_ERR(error))
+ 	{
+ 		process_error(error);
++		return;
+ 	}
++	process_connection();
++#endif
+ }
++
++void ASyncTCP::process_connection()
++{
++	m_bIsConnected = true;
++#ifdef WWW_ENABLE_SSL
++
++	if (!m_bSecure)
+ #endif
++	{
++		// RK: only if non-secure
++		boost::asio::socket_base::keep_alive option(true);
++		m_Socket.set_option(option);
++	}
++	OnConnect();
++	do_read_start();
++	do_write_start();
++}
+ 
+ void ASyncTCP::reconnect_start_timer()
+ {
+-	if (mIsReconnecting) return;
++	if (m_bIsReconnecting) return;
+ 
+-	if (mReconnectDelay != 0)
++	if (m_iReconnectDelay != 0)
+ 	{
+-		mIsReconnecting = true;
+-
+-		mReconnectTimer.expires_from_now(boost::posix_time::seconds(mReconnectDelay));
+-		mReconnectTimer.async_wait([this](auto &&err) { cb_reconnect_start(err); });
++		m_bIsReconnecting = true;
++
++		m_ReconnectTimer.expires_from_now(boost::posix_time::seconds(m_iReconnectDelay));
++		m_ReconnectTimer.async_wait(
++			[this](const boost::system::error_code& error) {
++				cb_reconnect_start(error);
++			}
++		);
+ 	}
+ }
+ 
+ void ASyncTCP::cb_reconnect_start(const boost::system::error_code& error)
+ {
+-	mIsReconnecting = false;
+-	mReconnectTimer.cancel();
+-	mTimeoutTimer.cancel();
++	m_bIsReconnecting = false;
++	m_ReconnectTimer.cancel();
++	m_TimeoutTimer.cancel();
+ 
+-	if (mIsConnected) return;
++	if (m_bIsConnected) return;
+ 	if (error) return; // timer was cancelled
+ 
+ 	do_close();
+-	connect(mIp, mPort);
++	connect(m_IP, m_Port);
+ }
+ 
+ 
+ void ASyncTCP::terminate(const bool silent)
+ {
+-	mIsTerminating = true;
++	m_bIsTerminating = true;
+ 	disconnect(silent);
+-	mTcpwork.reset();
+-	mIos.stop();
+-	if (mTcpthread)
++	m_Tcpwork.reset();
++	m_io_context.stop();
++	if (m_Tcpthread)
+ 	{
+-		mTcpthread->join();
+-		mTcpthread.reset();
++		m_Tcpthread->join();
++		m_Tcpthread.reset();
+ 	}
+-	mIsReconnecting = false;
+-	mIsConnected = false;
+-	mWriteQ.clear();
+-	mIsTerminating = false;
++	m_bIsReconnecting = false;
++	m_bIsConnected = false;
++	m_WriteQ.clear();
++	m_bIsTerminating = false;
+ }
+ 
+ void ASyncTCP::disconnect(const bool silent)
+ {
+-	mReconnectTimer.cancel();
+-	mTimeoutTimer.cancel();
+-	if (!mTcpthread) return;
++	m_ReconnectTimer.cancel();
++	m_TimeoutTimer.cancel();
++	if (!m_Tcpthread) return;
+ 
+ 	try
+ 	{
+-		mIos.post([this] { do_close(); });
++		boost::asio::post(m_io_context, 
++			[this] {
++				do_close();
++			}
++		);
+ 	}
+ 	catch (...)
+ 	{
+@@ -223,62 +251,68 @@ void ASyncTCP::disconnect(const bool silent)
+ 
+ void ASyncTCP::do_close()
+ {
+-	if (mIsReconnecting) {
++	if (m_bIsReconnecting) {
+ 		return;
+ 	}
+-	mReconnectTimer.cancel();
+-	mTimeoutTimer.cancel();
++	m_ReconnectTimer.cancel();
++	m_TimeoutTimer.cancel();
+ 	boost::system::error_code ec;
+ #ifdef WWW_ENABLE_SSL
+-	if (mSecure)
++	if (m_bSecure)
+ 	{
+-		if (mSslSocket->lowest_layer().is_open())
++		if (m_SslSocket->lowest_layer().is_open())
+ 		{
+-			mSslSocket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
+-			mSslSocket->lowest_layer().close(ec);
++			m_SslSocket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
++			m_SslSocket->lowest_layer().close(ec);
+ 		}
+ 	}
+ 	else
+ #endif
+ 	{
+-		if (mSocket.is_open())
++		if (m_Socket.is_open())
+ 		{
+-			mSocket.close(ec);
++			m_Socket.close(ec);
+ 		}
+ 	}
+ }
+ 
+ void ASyncTCP::do_read_start()
+ {
+-	if (mIsTerminating) return;
+-	if (!mIsConnected) return;
++	if (m_bIsTerminating) return;
++	if (!m_bIsConnected) return;
+ 
+ 	timeout_start_timer();
+ #ifdef WWW_ENABLE_SSL
+-	if (mSecure)
++	if (m_bSecure)
+ 	{
+-		mSslSocket->async_read_some(boost::asio::buffer(m_pRXBuffer, MAX_TCP_BUFFER_SIZE), [this](auto &&err, auto bytes) { cb_read_done(err, bytes); });
++		m_SslSocket->async_read_some(boost::asio::buffer(m_pRXBuffer, MAX_TCP_BUFFER_SIZE),
++			[this](const boost::system::error_code& error, size_t bytes_transferred) {
++				cb_read_done(error, bytes_transferred);
++			}
++		);
+ 	}
+ 	else
+ #endif
+ 	{
+-		mSocket.async_read_some(boost::asio::buffer(m_pRXBuffer, MAX_TCP_BUFFER_SIZE), [this](auto &&err, auto bytes) { cb_read_done(err, bytes); });
++		m_Socket.async_read_some(boost::asio::buffer(m_pRXBuffer, MAX_TCP_BUFFER_SIZE),
++			[this](const boost::system::error_code& error, size_t bytes_transferred) {
++				cb_read_done(error, bytes_transferred);
++			}
++		);
+ 	}
+ }
+ 
+ void ASyncTCP::cb_read_done(const boost::system::error_code& error, size_t bytes_transferred)
+ {
+-	if (mIsTerminating) return;
++	if (m_bIsTerminating) return;
+ 
+-	if (STATUS_OK(error))
+-	{
+-		OnData(m_pRXBuffer, bytes_transferred);
+-		do_read_start();
+-	}
+-	else
++	if (STATUS_ERR(error))
+ 	{
+ 		process_error(error);
++		return;
+ 	}
++	OnData(m_pRXBuffer, bytes_transferred);
++	do_read_start();
+ }
+ 
+ void ASyncTCP::write(const uint8_t* pData, size_t length)
+@@ -288,77 +322,66 @@ void ASyncTCP::write(const uint8_t* pData, size_t length)
+ 
+ void ASyncTCP::write(const std::string& msg)
+ {
+-	if (!mTcpthread) return;
++	if (!m_Tcpthread) return;
+ 
+-	mSendStrand.post([this, msg]() { cb_write_queue(msg); });
++	boost::asio::post(m_SendStrand, [this, msg]() { cb_write_queue(msg); });
+ }
+ 
+ void ASyncTCP::cb_write_queue(const std::string& msg)
+ {
+-	mWriteQ.push_back(msg);
++	m_WriteQ.push_back(msg);
+ 
+-	if (mWriteQ.size() == 1)
++	if (m_WriteQ.size() == 1)
+ 		do_write_start();
+ }
+ 
+ void ASyncTCP::do_write_start()
+ {
+-	if (mIsTerminating) return;
+-	if (!mIsConnected) return;
+-	if (mWriteQ.empty())
++	if (m_bIsTerminating) return;
++	if (!m_bIsConnected) return;
++	if (m_WriteQ.empty())
+ 		return;
+ 
+ 	timeout_start_timer();
+ #ifdef WWW_ENABLE_SSL
+-	if (mSecure) 
++	if (m_bSecure)
+ 	{
+-		boost::asio::async_write(*mSslSocket, boost::asio::buffer(mWriteQ.front()), [this](auto &&err, auto) { cb_write_done(err); });
++		boost::asio::async_write(*m_SslSocket, boost::asio::buffer(m_WriteQ.front()),
++			[this](const boost::system::error_code& error, std::size_t length) {
++				cb_write_done(error, length);
++			}
++		);
+ 	}
+ 	else
+ #endif
+ 	{
+-		boost::asio::async_write(mSocket, boost::asio::buffer(mWriteQ.front()), [this](auto &&err, auto) { cb_write_done(err); });
++		boost::asio::async_write(m_Socket, boost::asio::buffer(m_WriteQ.front()),
++			[this](const boost::system::error_code& error, std::size_t length) {
++				cb_write_done(error, length);
++			}
++		);
+ 	}
+ }
+ 
+-void ASyncTCP::cb_write_done(const boost::system::error_code& error)
++void ASyncTCP::cb_write_done(const boost::system::error_code& error, std::size_t /*length*/)
+ {
+-	if (mIsTerminating) return;
++	if (m_bIsTerminating) return;
+ 
+-	if (STATUS_OK(error))
+-	{
+-		mWriteQ.pop_front();
+-		do_write_start();
+-	}
+-	else
++	if (STATUS_ERR(error))
+ 	{
+ 		process_error(error);
++		return;
+ 	}
+-}
+-
+-void ASyncTCP::process_connection()
+-{
+-	mIsConnected = true;
+-#ifdef WWW_ENABLE_SSL
+-
+-	if (!mSecure)
+-#endif
+-	{
+-		// RK: only if non-secure
+-		boost::asio::socket_base::keep_alive option(true);
+-		mSocket.set_option(option);
+-	}
+-	OnConnect();
+-	do_read_start();
++	m_WriteQ.pop_front();
+ 	do_write_start();
+ }
+ 
+ void ASyncTCP::process_error(const boost::system::error_code& error)
+ {
+ 	do_close();
+-	if (mIsConnected)
++	if (m_bIsConnected)
+ 	{
+-		mIsConnected = false;
++		m_bIsConnected = false;
+ 		OnDisconnect();
+ 	}
+ 
+@@ -369,20 +392,23 @@ void ASyncTCP::process_error(const boost::system::error_code& error)
+ 	reconnect_start_timer();
+ }
+ 
+-/* timeout methods */
+ void ASyncTCP::timeout_start_timer()
+ {
+-	if (0 == mTimeoutDelay) {
++	if (0 == m_iTimeoutDelay) {
+ 		return;
+ 	}
+ 	timeout_cancel_timer();
+-	mTimeoutTimer.expires_from_now(boost::posix_time::seconds(mTimeoutDelay));
+-	mTimeoutTimer.async_wait([this](auto &&err) { timeout_handler(err); });
++	m_TimeoutTimer.expires_from_now(boost::posix_time::seconds(m_iTimeoutDelay));
++	m_TimeoutTimer.async_wait(
++		[this](const boost::system::error_code& error) {
++			timeout_handler(error);
++		}
++	);
+ }
+ 
+ void ASyncTCP::timeout_cancel_timer()
+ {
+-	mTimeoutTimer.cancel();
++	m_TimeoutTimer.cancel();
+ }
+ 
+ void ASyncTCP::timeout_handler(const boost::system::error_code& error)
+@@ -397,5 +423,5 @@ void ASyncTCP::timeout_handler(const boost::system::error_code& error)
+ 
+ void ASyncTCP::SetTimeout(const uint32_t Timeout)
+ {
+-	mTimeoutDelay = Timeout;
++	m_iTimeoutDelay = Timeout;
+ }
+diff --git hardware/ASyncTCP.h hardware/ASyncTCP.h
+index cf859bb..a8b3ae2 100644
+--- hardware/ASyncTCP.h
++++ hardware/ASyncTCP.h
+@@ -3,39 +3,31 @@
+ #include <stddef.h>			 // for size_t
+ #include <deque>			 // for write queue
+ #include <boost/asio/deadline_timer.hpp> // for deadline_timer
+-#include <boost/asio/io_service.hpp>	 // for io_service
++#include <boost/asio/io_context.hpp>	 // for io_context
+ #include <boost/asio/strand.hpp>	 // for strand
+ #include <boost/asio/ip/tcp.hpp>	 // for tcp, tcp::endpoint, tcp::s...
+ #include <boost/asio/ssl.hpp>		 // for secure sockets
+ #include <boost/asio/ssl/stream.hpp>	 // for secure sockets
+ #include <exception>			  // for exception
++#include <optional>			// for optional
+ 
+ #define ASYNCTCP_THREAD_NAME "ASyncTCP"
+ #define DEFAULT_RECONNECT_TIME 30
+ #define DEFAULT_TIMEOUT_TIME 60
+ 
+-namespace boost
+-{
+-	namespace system
+-	{
+-		class error_code;
+-	} // namespace system
+-} // namespace boost
+-
+ class ASyncTCP
+ {
+-      protected:
++protected:
+ 	ASyncTCP(bool secure = false);
+ 	virtual ~ASyncTCP();
+-
+-	void connect(const std::string &hostname, uint16_t port);
++	void connect(const std::string& hostname, uint16_t port);
+ 	void disconnect(bool silent = true);
+-	void write(const std::string &msg);
+-	void write(const uint8_t *pData, size_t length);
+-	void SetReconnectDelay(int32_t Delay = DEFAULT_RECONNECT_TIME);
++	void write(const std::string& msg);
++	void write(const uint8_t* pData, size_t length);
++	void SetReconnectDelay(const int32_t Delay = DEFAULT_RECONNECT_TIME);
+ 	bool isConnected()
+ 	{
+-		return mIsConnected;
++		return m_bIsConnected;
+ 	};
+ 	void terminate(bool silent = true);
+ 	void SetTimeout(uint32_t Timeout = DEFAULT_TIMEOUT_TIME);
+@@ -43,65 +35,61 @@ class ASyncTCP
+ 	// Callback interface to implement in derived classes
+ 	virtual void OnConnect() = 0;
+ 	virtual void OnDisconnect() = 0;
+-	virtual void OnData(const uint8_t *pData, size_t length) = 0;
+-	virtual void OnError(const boost::system::error_code &error) = 0;
+-
+-	boost::asio::io_service mIos; // protected to allow derived classes to attach timers etc.
++	virtual void OnData(const uint8_t* pData, size_t length) = 0;
++	virtual void OnError(const boost::system::error_code& error) = 0;
+ 
+-      private:
+-	void cb_resolve_done(const boost::system::error_code &err, boost::asio::ip::tcp::resolver::iterator endpoint_iterator);
+-	void connect_start(boost::asio::ip::tcp::resolver::iterator &endpoint_iterator);
+-	void cb_connect_done(const boost::system::error_code &error, boost::asio::ip::tcp::resolver::iterator &endpoint_iterator);
++	boost::asio::io_context m_io_context; // protected to allow derived classes to attach timers etc.
++private:
++	void handle_resolve(const boost::system::error_code& ec, const boost::asio::ip::tcp::resolver::results_type &results);
++	void handle_connect(const boost::system::error_code& error, const boost::asio::ip::tcp::endpoint& endpoint);
+ #ifdef WWW_ENABLE_SSL
+-	void cb_handshake_done(const boost::system::error_code &error);
++	void cb_handshake_done(const boost::system::error_code& error);
+ #endif
+ 
+-	/* timeout methods */
+ 	void timeout_start_timer();
+ 	void timeout_cancel_timer();
+ 	void reconnect_start_timer();
+-	void timeout_handler(const boost::system::error_code &error);
++	void timeout_handler(const boost::system::error_code& error);
+ 
+-	void cb_reconnect_start(const boost::system::error_code &error);
++	void cb_reconnect_start(const boost::system::error_code& error);
+ 
+ 	void do_close();
+ 
+ 	void do_read_start();
+-	void cb_read_done(const boost::system::error_code &error, size_t bytes_transferred);
++	void cb_read_done(const boost::system::error_code& error, size_t bytes_transferred);
+ 
+-	void cb_write_queue(const std::string &msg);
++	void cb_write_queue(const std::string& msg);
+ 	void do_write_start();
+-	void cb_write_done(const boost::system::error_code &error);
++	void cb_write_done(const boost::system::error_code& error, size_t length);
+ 
+ 	void process_connection();
+-	void process_error(const boost::system::error_code &error);
++	void process_error(const boost::system::error_code& error);
+ 
+-	bool mIsConnected = false;
+-	bool mIsReconnecting = false;
+-	bool mIsTerminating = false;
++	bool m_bIsConnected = false;
++	bool m_bIsReconnecting = false;
++	bool m_bIsTerminating = false;
+ 
+-	boost::asio::io_service::strand mSendStrand{ mIos };
+-	std::deque<std::string> mWriteQ; // we need a write queue to allow concurrent writes
++	boost::asio::io_context::strand m_SendStrand;
++	std::deque<std::string> m_WriteQ; // we need a write queue to allow concurrent writes
+ 
+ 	uint8_t* m_pRXBuffer = nullptr;
+ 
+-	int mReconnectDelay = DEFAULT_RECONNECT_TIME;
+-	int mTimeoutDelay = 0;
+-	boost::asio::deadline_timer mReconnectTimer{ mIos };
+-	boost::asio::deadline_timer mTimeoutTimer{ mIos };
++	int m_iReconnectDelay = DEFAULT_RECONNECT_TIME;
++	int m_iTimeoutDelay = 0;
++	boost::asio::deadline_timer m_ReconnectTimer;
++	boost::asio::deadline_timer m_TimeoutTimer;
+ 
+-	std::shared_ptr<std::thread> mTcpthread;
+-	std::shared_ptr<boost::asio::io_service::work> mTcpwork;
++	std::shared_ptr<std::thread> m_Tcpthread;
++	std::optional<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>> m_Tcpwork;
+ 
+ #ifdef WWW_ENABLE_SSL
+-	const bool mSecure;
++	const bool m_bSecure;
+ 	boost::asio::ssl::context mContext{ boost::asio::ssl::context::sslv23 };
+-	std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> mSslSocket; // the ssl socket
++	std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_SslSocket;
+ #endif
+-	boost::asio::ip::tcp::socket mSocket{ mIos };
+-	boost::asio::ip::tcp::endpoint mEndPoint;
+-	boost::asio::ip::tcp::resolver mResolver{ mIos };
++	boost::asio::ip::tcp::socket m_Socket;
++	boost::asio::ip::tcp::resolver m_Resolver;
+ 
+-	std::string mIp;
+-	uint16_t mPort;
++	std::string m_IP;
++	uint16_t m_Port;
+ };
+diff --git hardware/Arilux.cpp hardware/Arilux.cpp
+index 400f5a3..20bc373 100644
+--- hardware/Arilux.cpp
++++ hardware/Arilux.cpp
+@@ -79,7 +79,7 @@ void Arilux::InsertUpdateSwitch(const std::string &lightName, const int subType,
+ {
+ 	uint32_t sID;
+ 	try {
+-		sID = boost::asio::ip::address_v4::from_string(location).to_ulong();
++		sID = boost::asio::ip::make_address_v4(location).to_uint();
+ 	} catch (const std::exception &e) {
+ 		Log(LOG_ERROR, "Bad IP address: %s (%s)", location.c_str(), e.what());
+ 		return;
+@@ -112,8 +112,8 @@ bool Arilux::SendTCPCommand(uint32_t ip,std::vector<unsigned char> &command)
+ 	sum = sum & 0xFF;
+ 	command.push_back((unsigned char)sum);
+ 
+-	boost::asio::io_service io_service;
+-	boost::asio::ip::tcp::socket sendSocket(io_service);
++	boost::asio::io_context io_context;
++	boost::asio::ip::tcp::socket sendSocket(io_context);
+ 	boost::asio::ip::address_v4 address(ip);
+ 	boost::asio::ip::tcp::endpoint endpoint(address, 5577);
+ 	try
+diff --git hardware/Kodi.cpp hardware/Kodi.cpp
+index b528017..5ababf6 100644
+--- hardware/Kodi.cpp
++++ hardware/Kodi.cpp
+@@ -143,14 +143,14 @@ _eNotificationTypes	CKodiNode::CKodiStatus::NotificationType()
+ 	}
+ }
+ 
+-CKodiNode::CKodiNode(boost::asio::io_service *pIos, const int pHwdID, const int PollIntervalsec, const int pTimeoutMs,
++CKodiNode::CKodiNode(boost::asio::io_context *pIoc, const int pHwdID, const int PollIntervalsec, const int pTimeoutMs,
+ 	const std::string& pID, const std::string& pName, const std::string& pIP, const std::string& pPort)
+ {
+ 	m_Busy = false;
+ 	m_Stoppable = false;
+ 	m_PlaylistPosition = 0;
+ 
+-	m_Ios = pIos;
++	m_Ioc = pIoc;
+ 	m_HwdID = pHwdID;
+ 	m_DevID = atoi(pID.c_str());
+ 	sprintf(m_szDevID, "%X%02X%02X%02X", 0, 0, (m_DevID & 0xFF00) >> 8, m_DevID & 0xFF);
+@@ -581,11 +581,10 @@ void CKodiNode::handleConnect()
+ 		{
+ 			m_iMissedPongs = 0;
+ 			boost::system::error_code ec;
+-			boost::asio::ip::tcp::resolver resolver(*m_Ios);
+-			boost::asio::ip::tcp::resolver::query query(m_IP, (m_Port[0] != '-' ? m_Port : m_Port.substr(1)));
+-			auto iter = resolver.resolve(query);
+-			boost::asio::ip::tcp::endpoint endpoint = *iter;
+-			m_Socket = new boost::asio::ip::tcp::socket(*m_Ios);
++			boost::asio::ip::tcp::resolver resolver(*m_Ioc);
++			auto iter = resolver.resolve(m_IP, (m_Port[0] != '-' ? m_Port : m_Port.substr(1)));
++			boost::asio::ip::tcp::endpoint endpoint = *iter.begin();
++			m_Socket = new boost::asio::ip::tcp::socket(*m_Ioc);
+ 			m_Socket->connect(endpoint, ec);
+ 			if (!ec)
+ 			{
+@@ -975,19 +974,19 @@ void CKodi::Do_Work()
+ 					_log.Log(LOG_NORM, "Kodi: (%s) - Restarting thread.", node->m_Name.c_str());
+ 					boost::thread *tAsync = new boost::thread(&CKodiNode::Do_Work, node);
+ 					SetThreadName(tAsync->native_handle(), "KodiNode");
+-					m_ios.stop();
++					m_ioc.stop();
+ 				}
+ 				if (node->IsOn())
+ 					bWorkToDo = true;
+ 			}
+ 
+-			if (bWorkToDo && m_ios.stopped())  // make sure that there is a boost thread to service i/o operations
*** 3449 LINES SKIPPED ***