diff --git a/indi-ipx800/CMakeLists.txt b/indi-ipx800/CMakeLists.txt
new file mode 100644
index 000000000..55a0dd287
--- /dev/null
+++ b/indi-ipx800/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.13)
+PROJECT(indi_ipx800 C CXX)
+
+LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules/")
+##LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake_modules/")
+
+include(GNUInstallDirs)
+
+set(IPX800_VERSION_MAJOR 1)
+set(IPX800_VERSION_MINOR 0)
+
+find_package(INDI REQUIRED)
+
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h )
+
+include_directories( ${CMAKE_CURRENT_BINARY_DIR})
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR})
+include_directories( ${INDI_INCLUDE_DIR})
+
+include(CMakeCommon)
+
+########### IPX800 ###########
+set(indi_ipx800_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/indi_ipx800.cpp
+ )
+
+add_executable(indi_ipx800 ${indi_ipx800_SRCS})
+
+target_link_libraries(indi_ipx800 ${INDI_LIBRARIES})
+
+install(TARGETS indi_ipx800 RUNTIME DESTINATION bin )
+install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/indi_ipx800.xml DESTINATION ${INDI_DATA_DIR})
diff --git a/indi-ipx800/LICENSE b/indi-ipx800/LICENSE
new file mode 100644
index 000000000..11e8067e6
--- /dev/null
+++ b/indi-ipx800/LICENSE
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/indi-ipx800/README.md b/indi-ipx800/README.md
new file mode 100644
index 000000000..df9b395bd
--- /dev/null
+++ b/indi-ipx800/README.md
@@ -0,0 +1,12 @@
+# indi-ipx800
+Driver INDI for IPX800 domotic server
+Tested only with V4
+Requirements :
+- Needs M2M activated without header
+Todo :
+- V3 / V5 commands to implement
+
+
+cmake -DCMAKE_INSTALL_PREFIX=/usr [you folder with ipx800 sources files]
+make -j4
+sudo make install
diff --git a/indi-ipx800/config.h.cmake b/indi-ipx800/config.h.cmake
new file mode 100644
index 000000000..dee83376f
--- /dev/null
+++ b/indi-ipx800/config.h.cmake
@@ -0,0 +1,11 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/* Define INDI Data Dir */
+#cmakedefine INDI_DATA_DIR "@INDI_DATA_DIR@"
+
+/* Define Driver version */
+#define IPX800_VERSION_MAJOR @IPX800_VERSION_MAJOR@
+#define IPX800_VERSION_MINOR @IPX800_VERSION_MINOR@
+
+#endif // CONFIG_H
diff --git a/indi-ipx800/indi_ipx800.cpp b/indi-ipx800/indi_ipx800.cpp
new file mode 100644
index 000000000..9ea39a034
--- /dev/null
+++ b/indi-ipx800/indi_ipx800.cpp
@@ -0,0 +1,1162 @@
+/*******************************************************************************
+This file is part of the IPX800 INDI Driver.
+A driver for the IPX800 (GCE Electronics - https://www.gce-electronics.com)
+
+Copyright (C) 2024 Arnaud Dupont (aknotwot@protonmail.com)
+
+IPX800 INDI Driver is free software : you can redistribute it
+and / or modify it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation, either version 3 of the License,
+or (at your option) any later version.
+
+IPX800 INDI Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the Lesser GNU General Public License
+along with IPX800 INDI Driver. If not, see
+< http : //www.gnu.org/licenses/>.
+
+This driver is adapted from RollOff ino drivers developped by Jasem Mutlaq.
+The main purpose of this driver is to connect to IPX to driver, communicate, and manage
+opening and closing of roof.
+It is able to read IPX800 digital datas to check status and position of the roof.
+User can select, partially, for this first release, how IPX 800 is configured
+*******************************************************************************/
+
+#include "indi_ipx800.h"
+
+#include "indicom.h"
+#include "config.h"
+
+#include "connectionplugins/connectiontcp.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+//Network related includes:
+#include
+#include
+#include
+#include
+
+
+#define DEFAULT_POLLING_TIMER 2000
+
+// Read only
+#define ROOF_OPENED_SWITCH 0
+#define ROOF_CLOSED_SWITCH 1
+
+// Write only
+#define ROOF_OPEN_RELAY "OPEN"
+#define ROOF_CLOSE_RELAY "CLOSE"
+#define ROOF_ABORT_RELAY "ABORT"
+
+// Rollfino
+#define INACTIVE_STATUS 5
+
+// We declare an auto pointer to ipx800.
+std::unique_ptr ipx800(new Ipx800());
+
+void ISPoll(void *p);
+
+/*************************************************************************/
+/** Constructor **/
+/*************************************************************************/
+
+Ipx800::Ipx800() : INDI::InputInterface(this), INDI::OutputInterface(this)
+{
+
+ Roof_Status = UNKNOWN_STATUS;
+ Mount_Status = NONE_PARKED;
+
+ setVersion(IPX800_VERSION_MAJOR,IPX800_VERSION_MINOR);
+}
+
+/************************************************************************************
+*
+************************************************************************************/
+bool Ipx800::initProperties()
+{
+ LOG_INFO("Starting device...");
+
+ INDI::DefaultDevice::initProperties();
+ INDI::InputInterface::initProperties("Inputs&Outputs", DIGITAL_INTPUTS, 0, "Digital");
+ INDI::OutputInterface::initProperties("Inputs&Outputs", RELAYS_OUTPUTS, "Relay");
+
+ // SetParkDataType(PARK_NONE);
+ //addDebugControl();
+ addAuxControls(); // This is for standard controls not the local auxiliary switch
+ addConfigurationControl();
+
+
+ //Rolling list of possible functions managed by relays
+ IUFillSwitch(&RelaisInfoS[0], "Unused", "",ISS_ON);
+ IUFillSwitch(&RelaisInfoS[1], "Roof Engine Power", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[2], "Telescope Ventilation", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[3], "Heating Resistor 1", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[4], "Heating Resistor 2", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[5], "Roof Control Command", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[6], "Mount Power Supply", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[7], "Camera Power Supply ", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[8], "Other Power Supply 1", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[9], "Other Power Supply 2", "", ISS_OFF);
+ IUFillSwitch(&RelaisInfoS[10], "Other Power Supply 3", "", ISS_OFF);
+
+ //set default value of each relay
+ for(int i=0;i<11;i++)
+ {
+ Relais1InfoS[i] = RelaisInfoS[i];
+ Relais2InfoS[i] = RelaisInfoS[i];
+ Relais3InfoS[i] = RelaisInfoS[i];
+ Relais4InfoS[i] = RelaisInfoS[i];
+ Relais5InfoS[i] = RelaisInfoS[i];
+ Relais6InfoS[i] = RelaisInfoS[i];
+ Relais7InfoS[i] = RelaisInfoS[i];
+ Relais8InfoS[i] = RelaisInfoS[i];
+ }
+
+ //creation du selecteur de configuration des relais
+ IUFillSwitchVector(&RelaisInfoSP[0], Relais1InfoS, 11, getDeviceName(), "RELAY_1_CONFIGURATION", "Relay 1", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[1], Relais2InfoS, 11, getDeviceName(), "RELAY_2_CONFIGURATION", "Relay 2", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[2], Relais3InfoS, 11, getDeviceName(), "RELAY_3_CONFIGURATION", "Relay 3", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[3], Relais4InfoS, 11, getDeviceName(), "RELAIS_4_CONFIGURATION", "Relay 4", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[4], Relais5InfoS,11, getDeviceName(), "RELAIS_5_CONFIGURATION", "Relay 5", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[5], Relais6InfoS, 11, getDeviceName(), "RELAIS_6_CONFIGURATION", "Relay 6", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[6], Relais7InfoS,11, getDeviceName(), "RELAIS_7_CONFIGURATION", "Relay 7", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaisInfoSP[7], Relais8InfoS, 11, getDeviceName(), "RELAIS_8_CONFIGURATION", "Relay 8", RELAYS_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+
+ //creation liste deroulante entrees discretes
+ IUFillSwitch(&DigitalInputS[0], "Unused", "",ISS_ON);
+ IUFillSwitch(&DigitalInputS[1], "DEC Axis Parked", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[2], "RA Axis Parked", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[3], "Roof Opened", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[4], "Roof Closed", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[5], "Roof Engine Supplied", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[6], "Raspberry Power Supplied", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[7], "Main PC Supplied", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[8], "Other Digital 1", "", ISS_OFF);
+ IUFillSwitch(&DigitalInputS[9], "Other Digital 2", "", ISS_OFF);
+
+ //set default value of each digital input
+ for(int i=0;i<10;i++)
+ {
+ Digital1InputS[i] = DigitalInputS[i];
+ Digital2InputS[i] = DigitalInputS[i];
+ Digital3InputS[i] = DigitalInputS[i];
+ Digital4InputS[i] = DigitalInputS[i];
+ Digital5InputS[i] = DigitalInputS[i];
+ Digital6InputS[i] = DigitalInputS[i];
+ Digital7InputS[i] = DigitalInputS[i];
+ Digital8InputS[i] = DigitalInputS[i];
+ }
+
+ //creation du selecteur de configuration des entrées discretes
+ IUFillSwitchVector(&DigitalInputSP[0], Digital1InputS, 10, getDeviceName(), "DIGITAL_1_CONFIGURATION", "Digital 1", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[1], Digital2InputS, 10, getDeviceName(), "DIGITAL_2_CONFIGURATION", "Digital 2", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[2], Digital3InputS, 10, getDeviceName(), "DIGITAL_3_CONFIGURATION", "Digital 3", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[3], Digital4InputS, 10, getDeviceName(), "DIGITAL_4_CONFIGURATION", "Digital 4", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[4], Digital5InputS, 10, getDeviceName(), "DIGITAL_5_CONFIGURATION", "Digital 5", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[5], Digital6InputS, 10, getDeviceName(), "DIGITAL_6_CONFIGURATION", "Digital 6", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[6], Digital7InputS, 10, getDeviceName(), "DIGITAL_7_CONFIGURATION", "Digital 7", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitalInputSP[7], Digital8InputS, 10, getDeviceName(), "DIGITAL_8_CONFIGURATION", "Digital 8", DIGITAL_INPUT_CONFIGURATION_TAB,
+ IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+
+ //TO Manage in a next release
+ //IUFillText(&LoginPwdT[0], "LOGIN_VAL", "Login", "");
+ //IUFillText(&LoginPwdT[1], "PASSWD_VAL", "Password", "");
+ //IUFillTextVector(&LoginPwdTP, LoginPwdT, 2, getDeviceName(), "ACCESS_IPX", "IPX Access", OPTIONS_TAB, IP_RW, 0, IPS_IDLE);
+ //defineProperty(&LoginPwdTP);
+ //
+
+ //enregistrement des onglets de configurations
+ for(int i=0;i<8;i++)
+ {
+ defineProperty(&RelaisInfoSP[i]);
+ defineProperty(&DigitalInputSP[i]);
+ }
+
+ ///////////////////////////////////////////////
+ //Page de presentation de l'état des relais
+ ///////////////////////////////////////////////
+ //char name[3] = 'yyy', nameM[3] = 'xxx';
+ const char *name = "";
+ const char *nameM = "";
+
+ for(int i=0;i<2;i++)
+ {
+ if (i ==0) {
+ name = "On";
+ nameM = "ON"; }
+ else if (i ==1) {
+ name = "Off";
+ nameM = "OFF";
+ }
+ else {
+ LOG_ERROR ("Initialization Error...");
+ return false;
+ }
+ IUFillSwitch(&Relay1StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay2StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay3StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay4StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay5StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay6StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay7StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Relay8StateS[i], name, nameM, ISS_OFF);
+ }
+ IUFillSwitchVector(&RelaysStatesSP[0], Relay1StateS, 2, getDeviceName(), "RELAY_1_STATE", "Relay 1", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[1], Relay2StateS, 2, getDeviceName(), "RELAY_2_STATE", "Relay 2", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[2], Relay3StateS, 2, getDeviceName(), "RELAY_3_STATE", "Relay 3", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[3], Relay4StateS, 2, getDeviceName(), "RELAY_4_STATE", "Relay 4", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[4], Relay5StateS, 2, getDeviceName(), "RELAY_5_STATE", "Relay 5", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[5], Relay6StateS, 2, getDeviceName(), "RELAY_6_STATE", "Relay 6", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[6], Relay7StateS, 2, getDeviceName(), "RELAY_7_STATE", "Relay 7", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&RelaysStatesSP[7], Relay8StateS, 2, getDeviceName(), "RELAY_8_STATE", "Relay 8", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+
+ //////////////////////////////////////////////////////////
+ //page de presentation de l'état des entrées discretes
+ //////////////////////////////////////////////////////////
+
+ for(int i=0;i<2;i++)
+ {
+ if (i ==0) {
+ name = "On";
+ nameM = "ON"; }
+ else if (i ==1) {
+ name = "Off";
+ nameM = "OFF";
+ }
+ else {
+ LOG_ERROR ("Initialization Error...");
+ return false;
+ }
+ IUFillSwitch(&Digit1StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Digit2StateS[i], name, nameM,ISS_OFF);
+ IUFillSwitch(&Digit3StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Digit4StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Digit5StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Digit6StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Digit7StateS[i], name, nameM, ISS_OFF);
+ IUFillSwitch(&Digit8StateS[i], name, nameM,ISS_OFF);
+ }
+ IUFillSwitchVector(&DigitsStatesSP[0], Digit1StateS, 2, getDeviceName(), "DIGIT_1_STATE", "Digital 1", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[1], Digit2StateS, 2, getDeviceName(), "DIGIT_2_STATE", "Digital 2", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[2], Digit3StateS, 2, getDeviceName(), "DIGIT_3_STATE", "Digital 3", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[3], Digit4StateS, 2, getDeviceName(), "DIGIT_4_STATE", "Digital 4", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[4], Digit5StateS, 2, getDeviceName(), "DIGIT_5_STATE", "Digital 5", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[5], Digit6StateS, 2, getDeviceName(), "DIGIT_6_STATE", "Digital 6", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[6], Digit7StateS, 2, getDeviceName(), "DIGIT_7_STATE", "Digital 7", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+ IUFillSwitchVector(&DigitsStatesSP[7], Digit8StateS, 2, getDeviceName(), "DIGIT_8_STATE", "Digital 8", RAW_DATA_TAB,
+ IP_RO,ISR_1OFMANY, 60, IPS_IDLE);
+
+ // Initialisation des commutateurs ON/OFF
+ IUFillSwitch(&roofEnginePowerS[0], "POWER_ON", "On", ISS_OFF); // Par défaut sur OFF
+ IUFillSwitch(&roofEnginePowerS[1], "POWER_OFF", "Off", ISS_ON); // Par défaut sur ON
+
+ // Initialisation du vecteur de commutateurs avec ISRVector (type radio)
+ IUFillSwitchVector(&roofEnginePowerSP, roofEnginePowerS, 2, getDeviceName(),
+ "ROOF_POWER_MNGT", // Nom interne du vecteur
+ "Roof Engine Power Mngt", // Label affiché
+ "Options", // Groupe (onglet Options)
+ IP_RW, // Permissions (lecture/écriture)
+ ISR_1OFMANY, // Comportement exclusif (radio buttons)
+ 0, // Timeout (si nécessaire, mettre 0 pour ignorer)
+ IPS_IDLE); // État initial (inactif)
+
+ // Ajouter la propriété à l'onglet OPTIONS_TAB
+ defineProperty(&roofEnginePowerSP);
+
+ // Initialisation des commutateurs ON/OFF
+ IUFillSwitch(&IPXVersionS[0], "VERSION_3", "V3", ISS_OFF); // Par défaut sur OFF
+ IUFillSwitch(&IPXVersionS[1], "VERSION_4", "V4", ISS_ON); // Par défaut sur ON
+ IUFillSwitch(&IPXVersionS[2], "VERSION_5", "V5", ISS_OFF); // Par défaut sur ON
+
+ // Initialisation du vecteur de commutateurs avec ISRVector (type radio)
+ IUFillSwitchVector(&IPXVersionSP, IPXVersionS, 3, getDeviceName(),
+ "VERSION_SELECTION", // Nom interne du vecteur
+ "IPX800 Version", // Label affiché
+ "Main Control", // Groupe (onglet Options)
+ IP_RO, // Permissions (lecture/écriture)
+ ISR_1OFMANY, // Comportement exclusif (radio buttons)
+ 0, // Timeout (si nécessaire, mettre 0 pour ignorer)
+ IPS_IDLE); // État initial (inactif)
+
+ // Ajouter la propriété à l'onglet OPTIONS_TAB
+ defineProperty(&IPXVersionSP);
+
+ setDefaultPollingPeriod(DEFAULT_POLLING_TIMER);
+
+ tcpConnection = new Connection::TCP(this);
+ tcpConnection->setConnectionType(Connection::TCP::TYPE_TCP);
+ tcpConnection->setDefaultHost("192.168.1.1");
+ tcpConnection->setDefaultPort(666);
+
+ LOG_DEBUG("Updating Connection - Handshake");
+ tcpConnection->registerHandshake([&]()
+ {
+ LOG_DEBUG("Updating Connection - Call Handshake");
+ return Handshake();
+ }
+
+ );
+ registerConnection(tcpConnection);
+
+ return true;
+}
+
+bool Ipx800::Handshake()
+{
+ bool status = false;
+ bool res = false;
+ if (isSimulation())
+ {
+ LOGF_INFO("Connected successfuly to simulated %s.", getDeviceName());
+ return true;
+ }
+ else {
+ res = readCommand(GetR);
+ readAnswer();
+ if (res==false) {
+ LOG_ERROR("Handshake with IPX800 failed");
+ return false;
+ }
+ else {
+ LOG_INFO("Handshake with IPX800 successfull");
+ return true;
+ }
+ readAnswer();
+ if (!checkAnswer())
+ {
+ LOG_ERROR("Handshake with IPX800 failed - Wrong answer");
+ res = false;
+ }
+ else {
+ recordData(GetR);
+ }
+ }
+ return status;
+}
+
+void Ipx800::ISGetProperties(const char *dev)
+{
+ INDI::DefaultDevice::ISGetProperties(dev);
+
+}
+
+void ISSnoopDevice(XMLEle *root)
+{
+ ipx800->ISSnoopDevice(root);
+}
+
+bool Ipx800::ISSnoopDevice(XMLEle *root)
+{
+ return INDI::DefaultDevice::ISSnoopDevice(root);
+}
+
+/********************************************************************************************
+** Establish conditions on a connect.
+*********************************************************************************************/
+bool Ipx800::setupParams()
+{
+ LOG_DEBUG("Setting Params...");
+ updateObsStatus();
+
+
+ return true;
+}
+
+void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
+{
+ ipx800->ISNewSwitch(dev, name, states, names, n);
+}
+
+bool Ipx800::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
+{
+ ISwitch *myRelaisInfoS;
+ ISwitchVectorProperty myRelaisInfoSP;
+
+ ISwitch *myDigitalInputS;
+ ISwitchVectorProperty myDigitalInputSP;
+
+ int currentRIndex, currentDIndex =0;
+ bool infoSet = false;
+
+
+ // Make sure the call is for our device, and Fonctions Tab are initialized
+ if(dev != nullptr && !strcmp(dev,getDeviceName()))
+ {
+ if (INDI::OutputInterface::processSwitch(dev, name, states, names, n))
+ return true;
+
+ // Vérifie que l'événement concerne le vecteur de commutateurs Roof Engine Power Management - Options Tab
+ if (strcmp(name, roofEnginePowerSP.name) == 0)
+ {
+ // Parcours des commutateurs pour traiter les changements d'état
+ for (int i = 0; i < n; i++)
+ {
+ if (strcmp(names[i], "POWER_ON") == 0)
+ {
+ // Si POWER_ON est activé
+ if (states[i] == ISS_ON)
+ {
+ IDMessage(getDeviceName(), "Roof Engine Power Management: ON");
+ roofEnginePowerS[0].s = ISS_ON;
+ roofEnginePowerS[1].s = ISS_OFF; // Désactive l'autre switch
+ roofPowerManagement = true;
+ }
+ }
+ else if (strcmp(names[i], "POWER_OFF") == 0)
+ {
+ // Si POWER_OFF est activé
+ if (states[i] == ISS_ON)
+ {
+ IDMessage(getDeviceName(), "Roof Engine Power Management: OFF");
+ roofEnginePowerS[0].s = ISS_OFF;
+ roofEnginePowerS[1].s = ISS_ON; // Désactive l'autre switch
+ roofPowerManagement = false;
+ }
+ }
+ }
+
+ // Marque le vecteur de commutateurs comme mis à jour et notifie l'interface
+ roofEnginePowerSP.s = IPS_OK;
+ IDSetSwitch(&roofEnginePowerSP, nullptr);
+ }
+
+ for(int i=0;i<8;i++)
+ {
+ myRelaisInfoSP = ipx800->getMyRelayVector(i);
+ myDigitalInputSP = ipx800->getMyDigitsVector(i);
+
+ ////////////////////////////////////////////////////
+ // Relay Configuration
+ ////////////////////////////////////////////////////
+
+
+ if (!strcmp(name, myRelaisInfoSP.name))
+ {
+ LOGF_DEBUG("Relay function selected - SP : %s", myRelaisInfoSP.name);
+ IUUpdateSwitch(&myRelaisInfoSP,states,names,n);
+
+ myRelaisInfoS = myRelaisInfoSP.sp;
+ myRelaisInfoSP.s = IPS_OK;
+ IDSetSwitch(&myRelaisInfoSP,nullptr);
+
+ currentRIndex = IUFindOnSwitchIndex(&myRelaisInfoSP);
+
+ if (currentRIndex != -1) {
+ Relay_Fonction_Tab [currentRIndex] = i;
+
+ LOGF_DEBUG("Relay fonction index : %d", currentRIndex);
+
+ defineProperty(&RelaysStatesSP[i]);}
+ else
+ LOG_DEBUG("No On Switches found");
+
+ infoSet = true;
+ return true;
+ }
+
+ ////////////////////////////////////////////////////
+ // Digits Configuration
+ ////////////////////////////////////////////////////
+ if (!strcmp(name, myDigitalInputSP.name))
+ {
+ LOGF_DEBUG("Digital init : %s", myDigitalInputSP.name);
+ IUUpdateSwitch(&myDigitalInputSP,states,names,n);
+
+ myDigitalInputS = myDigitalInputSP.sp;
+ // myRelaisInfoS[].s = ISS_ON;
+ myDigitalInputSP.s = IPS_OK;
+ IDSetSwitch(&myDigitalInputSP,nullptr);
+ //sauvegarde de la configuration
+ currentDIndex = IUFindOnSwitchIndex(&myDigitalInputSP);
+ if (currentDIndex != -1) {
+ Digital_Fonction_Tab [currentDIndex] = i;
+ LOGF_DEBUG("Digital Inp. fonction index : %d", currentDIndex);
+ defineProperty(&DigitsStatesSP[i]);
+
+ }
+ else
+ LOG_DEBUG("No On Switches found");
+
+ infoSet = true;
+ return true;
+ }
+
+ }
+
+ LOG_DEBUG("ISNewSwitch - First Init + UpDate");
+ updateIPXData();
+
+ if (infoSet)
+ updateObsStatus();
+
+ }
+ return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
+
+}
+
+void ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
+{
+ ipx800->ISNewText(dev, name, texts, names, n);
+}
+
+bool Ipx800::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
+{
+ ////////////////////////////////////////////////////
+ // IPX Access - If password protected
+ // To manage in a next release
+ ////////////////////////////////////////////////////
+ /*
+ IText* myLoginT = ipx800->getMyLogin();
+ ITextVectorProperty myLoginVector = ipx800->getMyLoginVector();
+ if (dev && !strcmp(name, myLoginVector.name))
+ {
+ IUUpdateText(&myLoginVector, texts, names, n);
+ myLoginVector.s = IPS_OK;
+ myPasswd = myLoginT[1].text;
+ myLogin = myLoginT[0].text;
+ IDSetText(&myLoginVector, nullptr);
+ LOG_INFO("Password updated");
+ }
+ */
+
+
+ if (INDI::InputInterface::processText(dev, name, texts, names, n))
+ return true;
+ if (INDI::OutputInterface::processText(dev, name, texts, names, n))
+ return true;
+
+ return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
+ }
+
+void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
+{
+ ipx800->ISNewNumber(dev, name, values, names, n);
+}
+
+bool Ipx800::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
+{
+ if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
+ return true;
+
+ return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
+}
+
+///////////////////////////////////////////
+// When IPX800 is connected two more tabs appear : Relays Status
+// Digital inputs Status
+///////////////////////////////////////////
+bool Ipx800::Connect()
+{
+ bool status = INDI::DefaultDevice::Connect();
+ LOG_DEBUG("Connecting to device...");
+
+ return status;
+
+}
+
+bool Ipx800::Disconnect()
+{
+ bool status = INDI::DefaultDevice::Disconnect();
+
+ return status;
+}
+
+const char *Ipx800::getDefaultName()
+{
+ return (const char *)"Ipx800";
+}
+
+/////////////////////////////////////////
+// Used after connection / Disconnection
+/////////////////////////////////////////
+bool Ipx800::updateProperties()
+{
+ INDI::DefaultDevice::updateProperties();
+
+ LOG_DEBUG("updateProperties - Starting");
+ ///////////////////////////
+ if (isConnected())
+ { // Connect both states tabs
+ updateIPXData();
+ //firstFonctionTabInit();
+ //updateObsStatus();
+ INDI::InputInterface::updateProperties();
+ INDI::OutputInterface::updateProperties();
+ defineProperty(&roofEnginePowerSP);
+ defineProperty(&IPXVersionSP);
+ for(int i=0;i<8;i++)
+ {
+ defineProperty(&RelaysStatesSP[i]);
+ //MàJ DomeState
+ }
+ for(int i=0;i<8;i++)
+ {
+ defineProperty(&DigitsStatesSP[i]);
+ }
+
+ setupParams();
+
+ }
+ else { // Disconnect both "States TAB"
+
+ for(int i=0;i<8;i++)
+ {
+ deleteProperty(RelaysStatesSP[i].name);
+
+ }
+ for(int i=0;i<8;i++)
+ {
+ deleteProperty(DigitsStatesSP[i].name);
+ }
+ deleteProperty(roofEnginePowerSP.name);
+ deleteProperty(IPXVersionSP.name);
+
+ }
+ return true;
+}
+
+//////////////////////////////////////////
+// ** complete **
+//////////////////////////////////////////
+void Ipx800::TimerHit()
+{
+
+ if (!isConnected()) {
+ return; // No need to reset timer if we are not connected anymore
+ }
+
+ updateIPXData();
+
+ SetTimer(getPollingPeriod());
+}
+//////////////////////////////////////
+/* Save conf */
+bool Ipx800::saveConfigItems(FILE *fp)
+{
+
+ INDI::DefaultDevice::saveConfigItems(fp);
+ //IUSaveConfigText(fp, &LoginPwdTP);
+
+ /** sauvegarde de la configuration des relais et entrées discretes **/
+ ////////////////////////////
+ for(int i=0;i<8;i++)
+ {
+ IUSaveConfigSwitch(fp, &RelaisInfoSP[i]);
+ IUSaveConfigSwitch(fp, &DigitalInputSP[i]);
+ }
+ INDI::InputInterface::saveConfigItems(fp);
+ INDI::OutputInterface::saveConfigItems(fp);
+ return true;////////
+}
+
+//////////////////////////////////////
+/* readCommand */
+bool Ipx800::readCommand(IPX800_command rCommand) {
+
+ bool rc = false;
+ std::string ipx_url = "";
+ //int bytesWritten = 0, totalBytes = 0;
+ switch (rCommand) {
+ case GetR :
+ ipx_url = "Get=R";
+ break;
+ case GetD :
+ ipx_url = "Get=D";
+ break;
+ default :
+ {
+ LOGF_ERROR("readCommand - Unknown Command %s", rCommand);
+ return false;
+ }
+ }
+ LOGF_DEBUG ("readCommand - Sending %s",ipx_url.c_str());
+ rc = writeTCP(ipx_url);
+
+ return rc;
+};
+
+//////////////////////////////////////
+/* writeCommand */
+bool Ipx800::writeCommand(IPX800_command wCommand, int toSet) {
+
+ std::string ipx_url;
+ std::string number;
+
+
+ bool rc = false;
+ //int bytesWritten = 0, totalBytes = 0;
+ if (toSet <10)
+ number = "0"+ std::to_string(toSet);
+ else {
+ number = std::to_string(toSet);
+ }
+ switch (wCommand) {
+ case SetR :
+ ipx_url = "SetR=" + number ;
+ LOGF_DEBUG ("Sending Set R %s",number.c_str());
+ break;
+ case ClearR :
+ ipx_url = "ClearR=" + number;
+ LOGF_DEBUG ("Sending Clear R %s",number.c_str());
+ break;
+ default :
+ {
+ LOGF_ERROR("Unknown Command %s", wCommand);
+ return false;
+ }
+
+ }
+ rc = writeTCP(ipx_url);
+
+ return rc;
+};
+//////////////////////////////////////
+/* readAnswer */
+// TCP Answer reading
+void Ipx800::readAnswer(){
+ int received = 0;
+ int bytes, total = 0;
+ int portFD = tcpConnection->getPortFD();
+ char tmp[58] = "";
+ total = 58;
+ int i = 0;
+ do {
+ bytes = read(portFD,tmp+received,total-received);
+
+ if (bytes < 0) {
+ LOGF_ERROR("readAnswer - ERROR reading response from socket : %s", strerror(errno));
+ //std::this_thread::sleep_for(std::chrono::milliseconds(500));
+ i++;
+ if (i>2)
+ break;
+ }
+ else if (bytes == 0) {
+ LOG_DEBUG("readAnswer : end of stream");
+ break; }
+ received+=bytes;
+ } while (received < total);
+
+ LOGF_DEBUG("readAnswer - Longeur reponse : %i", received);
+
+ strncpy(tmpAnswer,tmp,8);
+
+ LOGF_DEBUG ("readAnswer - Reponse reçue : %s", tmpAnswer);
+
+ };
+
+//////////////////////////////////////
+/* recordData */
+void Ipx800::recordData(IPX800_command recCommand) {
+ int i = -1;
+ int tmpDR = UNUSED_DIGIT;
+ switch (recCommand) {
+ case GetD :
+ for (i=0;i<8;i++){
+ DigitsStatesSP[i].s = IPS_OK;
+ DigitalInputsSP[i].reset();
+ if (tmpAnswer[i] == '0') {
+ LOGF_DEBUG("recordData - Digital Input N° %d is %s",i+1,"OFF");
+ DigitalInputsSP[i][0].setState(ISS_ON);
+ DigitsStatesSP[i].sp[0].s = ISS_OFF;
+ DigitsStatesSP[i].sp[1].s = ISS_ON;
+ digitalState[i] = false;}
+ else if(tmpAnswer[i] == '1'){
+ LOGF_DEBUG("recordData - Digital Input N° %d is %s",i+1,"ON");
+ DigitalInputsSP[i][1].setState(ISS_ON);
+ DigitsStatesSP[i].sp[0].s = ISS_ON ;
+ DigitsStatesSP[i].sp[1].s = ISS_OFF;
+ digitalState[i] = true;
+ }
+ tmpAnswer[i] = ' ';
+ DigitalInputsSP[i].setState(IPS_OK);
+ DigitalInputsSP[i].apply();
+ defineProperty(&DigitsStatesSP[i]);
+ DigitsStatesSP[i].s = IPS_OK;
+ IDSetSwitch(&DigitsStatesSP[i], nullptr);
+ }
+
+ for (i=0;i<10;i++) {
+ tmpDR = Digital_Fonction_Tab[i];
+ if (tmpDR >= 0) {
+
+ if (tmpDR == ROOF_ENGINE_POWERED ) {
+ DigitalInputsSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].reset();
+ if (DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].sp[0].s == ISS_OFF) {
+ LOG_DEBUG("recordData - inverting ROOF_ENGINE_POWERED TO ON");
+
+ DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].sp[0].s = ISS_ON;
+ DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].sp[1].s = ISS_OFF;
+ digitalState[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]] = true;
+ DigitalInputsSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]][1].setState(ISS_ON);}
+ else {
+ LOG_DEBUG("recordData - inverting ROOF_ENGINE_POWERED TO OFF");
+ DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].sp[0].s = ISS_OFF;
+ DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].sp[1].s = ISS_ON;
+ digitalState[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]] = false;
+ DigitalInputsSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]][0].setState(ISS_ON);}
+
+
+ DigitalInputsSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].setState(IPS_OK);
+ DigitalInputsSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].apply();
+ defineProperty(&DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]]);
+ DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].s = IPS_OK;
+ IDSetSwitch(&DigitsStatesSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]], nullptr);
+
+ }
+ else if(tmpDR == RASPBERRY_SUPPLIED) {
+ DigitalInputsSP[Digital_Fonction_Tab[ROOF_ENGINE_POWERED]].reset();
+ if (DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].sp[0].s == ISS_OFF) {
+ LOG_DEBUG("recordData - inverting RASPBERRY_SUPPLIED TO ON");
+ DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].sp[0].s = ISS_ON;
+ DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].sp[1].s = ISS_OFF;
+ digitalState[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]] = true;
+ DigitalInputsSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]][1].setState(ISS_ON);}
+ else {
+ LOG_DEBUG("recordData - inverting RASPBERRY_SUPPLIED TO OFF");
+ DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].sp[0].s = ISS_OFF;
+ DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].sp[1].s = ISS_ON;
+ digitalState[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]] = false;
+ DigitalInputsSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]][0].setState(ISS_ON);}
+
+ DigitalInputsSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].setState(IPS_OK);
+ DigitalInputsSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].apply();
+ defineProperty(&DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]]);
+ DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]].s = IPS_OK;
+ IDSetSwitch(&DigitsStatesSP[Digital_Fonction_Tab[RASPBERRY_SUPPLIED]], nullptr);
+
+ }
+ else if (tmpDR == MAIN_PC_SUPPLIED) {
+ DigitalInputsSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].reset();
+ if (DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].sp[0].s == ISS_OFF) {
+ LOG_DEBUG("recordData - inverting MAIN_PC_SUPPLIED TO ON");
+ DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].sp[0].s = ISS_ON;
+ DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].sp[1].s = ISS_OFF;
+ digitalState[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]] = true;
+ DigitalInputsSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]][1].setState(ISS_ON);}
+ else {
+ LOG_DEBUG("recordData - inverting MAIN_PC_SUPPLIED TO OFF");
+ DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].sp[0].s = ISS_OFF;
+ DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].sp[1].s = ISS_ON;
+ digitalState[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]] = false;
+ DigitalInputsSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]][0].setState(ISS_ON);}
+
+ DigitalInputsSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].setState(IPS_OK);
+ DigitalInputsSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].apply();
+ defineProperty(&DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]]);
+ DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]].s = IPS_OK;
+ IDSetSwitch(&DigitsStatesSP[Digital_Fonction_Tab[MAIN_PC_SUPPLIED]], nullptr);
+
+ }
+ }
+ else {
+ LOGF_WARN("Wrong Digital ROOF_ENGINE_POWERED initialisation. Now it's = %d",tmpDR);
+ }
+
+ enginePowered = digitalState[Digital_Fonction_Tab [ROOF_ENGINE_POWERED]];
+ }
+ break;
+ case GetR :
+ for (int i=0;i<8;i++){
+ RelaysStatesSP[i].s = IPS_OK;
+ DigitalOutputsSP[i].reset();
+ if (tmpAnswer[i] == '0') {
+ LOGF_DEBUG("recordData - Relay N° %d is %s",i+1,"OFF");
+ RelaysStatesSP[i].sp[0].s = ISS_OFF;
+ RelaysStatesSP[i].sp[1].s = ISS_ON;
+ DigitalOutputsSP[i][0].setState(ISS_ON);
+ //relayState[i]= false;
+ }
+ else {
+ LOGF_DEBUG("recordData - Relay N° %d is %s",i+1,"ON");
+ RelaysStatesSP[i].sp[0].s = ISS_ON;
+ RelaysStatesSP[i].sp[1].s = ISS_OFF;
+ DigitalOutputsSP[i][1].setState(ISS_ON);
+ //relayState[i]=true;
+ }
+ tmpAnswer[i] = ' ';
+ DigitalOutputsSP[i].setState(IPS_OK);
+ DigitalOutputsSP[i].apply();
+ RelaysStatesSP[i].s = IPS_OK;
+
+ IDSetSwitch(&RelaysStatesSP[i], nullptr);
+ //defineProperty(&RelaysStatesSP[i]);
+
+ }
+ break;
+ default :
+ LOGF_ERROR("recordData - Unknown Command %s", recCommand);
+ break;
+ }
+
+ LOG_DEBUG("recordData - Switches States Recorded");
+
+};
+
+//////////////////////////////////////
+/* writeTCP Write Command on TCP socket */
+bool Ipx800::writeTCP(std::string toSend) {
+
+ int bytesWritten = 0, totalBytes = 0;
+ totalBytes = toSend.length();
+ int portFD = tcpConnection->getPortFD();
+
+ LOGF_DEBUG("writeTCP - Command to send %s", toSend.c_str());
+ LOGF_DEBUG ("writeTCP - Numéro de socket %i", portFD);
+
+ if (!isSimulation()) {
+ while (bytesWritten < totalBytes)
+ {
+ int bytesSent = write(portFD, toSend.c_str(), totalBytes - bytesWritten);
+ if (bytesSent >= 0)
+ bytesWritten += bytesSent;
+
+ else
+ {
+ LOGF_ERROR("writeTCP - Error request to IPX800. %s", strerror(errno));
+ return false;
+ }
+ }
+ }
+
+ LOGF_DEBUG ("writeTCP - bytes to send : %s", toSend.c_str());
+ LOGF_DEBUG ("writeTCP - Number of bytes sent : %d", bytesWritten);
+ return true;
+}
+
+
+
+//////////////////////////////////////
+/* updateIPXData */
+/* prepare commands to update Inputs Status */
+//////////////////////////////////////
+bool Ipx800::updateIPXData()
+{
+ bool res = false;
+ LOG_DEBUG("Updating IPX Data...");
+
+ res = UpdateDigitalOutputs();
+ if (res==false) {
+ LOG_ERROR("updateIPXData - Send Command GetR failed");
+ return false;
+ }
+
+ res = UpdateDigitalInputs();
+ if (res==false) {
+ LOG_ERROR("updateIPXData - Send Command GetD failed");
+ return false;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////
+/* updateObsStatus */
+void Ipx800::updateObsStatus()
+{
+
+}
+
+//////////////////////////////////////
+/* randomInit */
+bool Ipx800::firstFonctionTabInit()
+{
+ int currentDIndex = -1;
+ int currentRIndex = -1;
+ //int cptD, cptR =0;
+ for(int i=0;i<8;i++)
+ {
+ currentRIndex = IUFindOnSwitchIndex(&RelaisInfoSP[i]);
+ if (currentRIndex != -1) {
+ Relay_Fonction_Tab [currentRIndex] = i;
+ LOGF_DEBUG("firstFonctionTabInit - Relay %d is supporting function %d ",i+1, currentRIndex);
+ currentRIndex = -1;
+ //if (currentRIndex >0)
+ // cptR = cptR +1;
+ }
+ else
+ LOGF_DEBUG("firstFonctionTabInit - Function unknown for Relay %d", i+1);
+
+ currentDIndex = IUFindOnSwitchIndex(&DigitalInputSP[i]);
+ if (currentDIndex != -1) {
+ Digital_Fonction_Tab [currentDIndex] = i;
+ LOGF_DEBUG("firstFonctionTabInit - Digital Input %d is supporting function %d ",i+1, currentDIndex);
+ currentDIndex = -1;
+ //if (currentDIndex >0)
+ // cptD = cptD +1;
+ }
+ else
+ LOGF_DEBUG("firstFonctionTabInit - Function unknown for Digital Input %d", i+1);
+ }
+
+ return true;
+}
+
+//////////////////////////////////////
+/* checkAnswer */
+bool Ipx800::checkAnswer()
+{
+ for (int i=0;i<8;i++)
+ {
+ if ((tmpAnswer[i] == '0') || (tmpAnswer[i] == '1'))
+ {
+ return true;
+ }
+ else {
+ LOGF_ERROR("Wrong data in IPX answer : %s", tmpAnswer[i]);
+ return false;
+ }
+ }
+
+ return true;
+}
+/*
+Password Management for a next release
+IText* Ipx800::getMyLogin()
+{
+ return LoginPwdT;
+}
+
+ITextVectorProperty Ipx800::getMyLoginVector()
+{
+ return LoginPwdTP;
+}
+*/
+
+ISwitchVectorProperty Ipx800::getMyRelayVector(int i)
+{
+ return RelaisInfoSP[i];
+}
+
+ISwitchVectorProperty Ipx800::getMyDigitsVector(int i)
+{
+ return DigitalInputSP[i];
+}
+
+
+//////////////////////////////////////
+/* UpdateDigitalInputs */
+bool Ipx800::UpdateDigitalInputs()
+{
+ // update of all digital inputs
+ bool res = readCommand(GetD);
+ readAnswer();
+ if (res==false) {
+ LOG_ERROR("UpdateDigitalInputs - Send Command GetD failed");
+ return res;
+ }
+ else {
+ LOG_DEBUG("UpdateDigitalInputs - Send Command GetD successfull");
+
+ if (!checkAnswer())
+ {
+ LOG_ERROR("UpdateDigitalInputs - Wrong Command GetD send");
+ res = false;
+ }
+ else {
+ recordData(GetD);
+ }
+ }
+ return true;
+}
+
+bool Ipx800::UpdateAnalogInputs()
+{
+ return true;
+}
+
+//////////////////////////////////////
+/* UpdateDigitalOutputs */
+// Update Relays Status
+bool Ipx800::UpdateDigitalOutputs()
+{
+ // update of all digital inputs
+ bool res = readCommand(GetR);
+ readAnswer();
+ if (res==false) {
+ LOG_ERROR("UpdateDigitalOutputs - Send Command GetR failed");
+
+ return res;
+ }
+ else {
+ LOG_DEBUG("UpdateDigitalOutputs - Send Command GetR successfull");
+
+ if (!checkAnswer())
+ {
+ LOG_ERROR("UpdateDigitalOutputs - Wrong Command GetR send");
+ res = false;
+ }
+ else {
+ recordData(GetR);
+ }
+ }
+ return true;
+}
+
+//////////////////////////////////////
+/* CommandOutput */
+//
+bool Ipx800::CommandOutput(uint32_t index, OutputState command)
+{
+ //check index is controling enginepower
+ int relayNumber = index+1;
+ bool rc = false;
+ //modifier pour permettre l'emission de commande pour toutes les commandes....sans lien avec le moteur
+ if (roofPowerManagement && enginePowered==false && static_cast(Relay_Fonction_Tab[ROOF_CONTROL_COMMAND])==static_cast(index)) {
+ LOG_WARN("Please switch on roof engine power");
+ return false; }
+ else {
+ if (command == INDI::OutputInterface::On)
+ rc = writeCommand(SetR, relayNumber);
+ else
+ rc = writeCommand(ClearR, relayNumber);
+ readAnswer();
+ return rc;
+ }
+
+}
diff --git a/indi-ipx800/indi_ipx800.h b/indi-ipx800/indi_ipx800.h
new file mode 100644
index 000000000..e2195bfc1
--- /dev/null
+++ b/indi-ipx800/indi_ipx800.h
@@ -0,0 +1,224 @@
+/*******************************************************************************
+This file is part of the IPX800 INDI Driver.
+A driver for the IPX800 (GCE Electronics - https://www.gce-electronics.com)
+
+Copyright (C) 2024 Arnaud Dupont (aknotwot@protonmail.com)
+
+IPX800 INDI Driver is free software : you can redistribute it
+and / or modify it under the terms of the GNU Lesser General Public License as
+published by the Free Software Foundation, either version 3 of the License,
+or (at your option) any later version.
+
+IPX800 INDI Driver is distributed in the hope that it will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with IPX800 INDI Driver. If not, see
+< http : //www.gnu.org/licenses/>.
+
+*******************************************************************************/
+#pragma once
+
+#include "defaultdevice.h"
+#include
+#include
+#include
+#include
+
+class Ipx800 : public INDI::DefaultDevice, public INDI::InputInterface, public INDI::OutputInterface
+
+{
+ public:
+
+ Ipx800();
+ virtual ~Ipx800() override = default;
+
+ virtual bool initProperties() override;
+ virtual bool updateProperties() override;
+ const char *getDefaultName() override;
+
+ virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override;
+ virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override;
+ virtual bool ISNewNumber(const char *dev,const char *name,double values[],char *names[],int n) override;
+
+ //TO CONFIRM
+
+ virtual bool ISSnoopDevice(XMLEle *root) override;
+ virtual void ISGetProperties(const char *dev) ;
+ protected:
+
+ bool Handshake();
+ bool Connect() override;
+ bool Disconnect() override;
+ void TimerHit() override;
+
+ enum IPX800_command {
+ GetR = 1 << 0,
+ GetD = 1 << 1,
+ SetR = 1 << 2,
+ ClearR = 1 << 3
+ } ;
+
+ ///////////////////////////////////////////
+ // IPX800 Communication
+ ///////////////////////////////////////////
+ bool updateIPXData();
+ void updateObsStatus();
+ bool readCommand(IPX800_command);
+ bool writeCommand(IPX800_command, int toSet);
+ bool checkAnswer();
+ void readAnswer();
+ void recordData(IPX800_command command);
+ bool writeTCP(std::string toSend);
+ bool firstFonctionTabInit();
+
+ virtual bool UpdateDigitalInputs() override;
+ virtual bool UpdateAnalogInputs() override; // IPX800 Analog Inputs not managed
+ virtual bool UpdateDigitalOutputs() override;
+ virtual bool CommandOutput(uint32_t index, OutputState command) override;
+ virtual bool saveConfigItems(FILE *fp) override;
+
+ private:
+ bool roofPowerManagement = false;
+ Connection::TCP *tcpConnection {nullptr};
+
+ // TO manage Password in a next release
+ //IText* getMyLogin();
+ //ITextVectorProperty getMyLoginVector();
+
+ ISwitchVectorProperty getMyRelayVector(int i);
+ ISwitchVectorProperty getMyDigitsVector(int i);
+
+ // List of possible commands
+ enum {
+ UNUSED_RELAY,
+ ROOF_ENGINE_POWER_SUPPLY,
+ TUBE_VENTILATION,
+ HEATING_RESISTOR_1,
+ HEATING_RESISTOR_2,
+ ROOF_CONTROL_COMMAND,
+ MOUNT_POWER_SUPPLY,
+ CAM_POWER_SUPPLY,
+ OTHER_POWER_SUPPLY_1,
+ OTHER_POWER_SUPPLY_2,
+ OTHER_POWER_SUPPLY_3
+ } IPXRelaysCommands;
+
+ // List of possibles digital inputs
+ enum {
+ UNUSED_DIGIT,
+ DEC_AXIS_PARKED,
+ RA_AXIS_PARKED,
+ ROOF_OPENED,
+ ROOF_CLOSED,
+ ROOF_ENGINE_POWERED,
+ RASPBERRY_SUPPLIED,
+ MAIN_PC_SUPPLIED,
+ OTHER_DIGITAL_1,
+ OTHER_DIGITAL_2 } IPXDigitalRead;
+
+ char tmpAnswer[8]= {0};
+ bool setupParams();
+ float CalcTimeLeft(timeval);
+
+ ISState fullOpenLimitSwitch { ISS_ON };
+ ISState fullClosedLimitSwitch { ISS_OFF };
+ double MotionRequest { 0 };
+ struct timeval MotionStart { 0, 0 };
+
+ ISwitch RelaisInfoS[11] {};
+ ISwitch Relais1InfoS[11], Relais2InfoS[11],Relais3InfoS[11],Relais4InfoS[11],Relais5InfoS[11],Relais6InfoS[11],Relais7InfoS[11],Relais8InfoS[11] {};
+ ISwitchVectorProperty RelaisInfoSP[8] {};
+
+ ISwitch DigitalInputS[10] {};
+ ISwitch Digital1InputS[10], Digital2InputS[10], Digital3InputS[10], Digital4InputS[10], Digital5InputS[10], Digital6InputS[10], Digital7InputS[10], Digital8InputS[10];
+ ISwitchVectorProperty DigitalInputSP[8] {};
+
+ ISwitch Relay1StateS[2],Relay2StateS[2],Relay3StateS[2],Relay4StateS[2],Relay5StateS[2],Relay6StateS[2],Relay7StateS[2],Relay8StateS[2] ;
+ ISwitchVectorProperty RelaysStatesSP[8] ;
+ ISwitch Digit1StateS[2],Digit2StateS[2],Digit3StateS[2],Digit4StateS[2],Digit5StateS[2],Digit6StateS[2],Digit7StateS[2],Digit8StateS[2] ;
+ ISwitchVectorProperty DigitsStatesSP[8] ;
+
+ //TO manage Password in a next release
+ //IText LoginPwdT[2];
+ //ITextVectorProperty LoginPwdTP;
+ //std::string myPasswd = "";
+ //std::string myLogin = "";
+
+ enum {
+ ROOF_IS_OPENED ,
+ ROOF_IS_CLOSED ,
+ UNKNOWN_STATUS
+ }
+ Roof_Status;
+
+ enum {
+ RA_PARKED ,
+ DEC_PARKED ,
+ BOTH_PARKED ,
+ NONE_PARKED
+ }
+ Mount_Status;
+
+ const char *ROLLOFF_TAB = "Roll Off";
+ const char *RELAYS_CONFIGURATION_TAB = "Relays Outputs";
+ const char *DIGITAL_INPUT_CONFIGURATION_TAB = "Digital Inputs";
+ const char *RAW_DATA_TAB = "Status";
+
+ const int DIGITAL_INTPUTS = 8;
+ const int RELAYS_OUTPUTS = 8;
+
+
+ // Relay_Fonction_Tab provide relay output in charge of the function
+ // fonctions are ordered arbitrarly as following.
+ // Only 8 functions applicable - only 8 relays output on IPX800
+ // Others are spares
+ int Relay_Fonction_Tab [11] = {0};
+ /* 0: UNUSED_RELAY,
+ ROOF_ENGINE_POWER_SUPPLY,
+ TUBE_VENTILATION,
+ HEATING_RESISTOR_1,
+ HEATING_RESISTOR_2,
+ ROOF_CONTROL_COMMAND,
+ MOUNT_POWER_SUPPLY,
+ CAM_POWER_SUPPLY,
+ OTHER_POWER_SUPPLY_1,
+ OTHER_POWER_SUPPLY_2,
+ 10 : OTHER_POWER_SUPPLY_3 */
+
+ // Digital_Fonction_Tab provide digital input in charge of the function
+ // fonctions are ordered arbitrarly as following.
+ // Only 8 functions applicable - only 8 digital inputs on IPX800
+ // Others are spares
+ int Digital_Fonction_Tab [11] = {0};
+ /*
+ 0: UNUSED_DIGIT,
+ DEC_AXIS_PARKED,
+ RA_AXIS_PARKED,
+ ROOF_OPENED,
+ ROOF_CLOSED,
+ ROOF_ENGINE_POWERED,
+ RASPBERRY_SUPPLIED,
+ MAIN_PC_SUPPLIED,
+ OTHER_DIGITAL_1,
+ 9 : OTHER_DIGITAL_2
+ */
+
+ // status of each relay output and digital input
+ // ordered the same way in IPX800
+ bool relayState[8];
+ bool digitalState[8];
+
+ int mount_Status = RA_PARKED | DEC_PARKED | BOTH_PARKED | NONE_PARKED;
+ int roof_Status = ROOF_IS_OPENED | ROOF_IS_CLOSED | UNKNOWN_STATUS;
+ bool enginePowered = false ; // True = on / false = Off
+ bool first_Start = false;
+
+ ISwitch IPXVersionS[5];
+ ISwitch roofEnginePowerS[2];
+ ISwitchVectorProperty IPXVersionSP, roofEnginePowerSP;
+
+
+};
diff --git a/indi-ipx800/indi_ipx800.xml b/indi-ipx800/indi_ipx800.xml
new file mode 100644
index 000000000..280187b8d
--- /dev/null
+++ b/indi-ipx800/indi_ipx800.xml
@@ -0,0 +1,9 @@
+
+
+
+
+ indi_ipx800
+ 0.5
+
+
+
diff --git a/indi-ipx800/install_manifest.txt b/indi-ipx800/install_manifest.txt
new file mode 100644
index 000000000..098282321
--- /dev/null
+++ b/indi-ipx800/install_manifest.txt
@@ -0,0 +1,2 @@
+/usr/bin/indi_ipx800
+/usr/share/indi/indi_ipx800.xml
\ No newline at end of file