This commit is contained in:
sebseb7
2025-12-24 23:11:24 +01:00
parent 23a6f900ec
commit 0d08a4d924
5 changed files with 202 additions and 186 deletions

View File

@@ -35,14 +35,15 @@ export class ACInfinityClient {
async login() {
try {
// AC Infinity API does not accept passwords greater than 25 characters
const normalizedPassword = this.password.substring(0, 25);
// AC Infinity API does not accept passwords greater than 25 characters - UPDATE: Reference impl uses full password?
// const normalizedPassword = this.password.substring(0, 25);
const normalizedPassword = this.password;
const response = await fetch(`${this.host}${API_URL_LOGIN}`, {
method: 'POST',
headers: {
'User-Agent': 'ACController/1.9.7 (com.acinfinity.humiture; build:533; iOS 18.5.0) Alamofire/5.10.2',
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
appEmail: this.email,
@@ -63,6 +64,7 @@ export class ACInfinityClient {
console.log('[AC] Successfully logged in to AC Infinity API');
return this.userId;
} catch (error) {
console.error('[AC] Login error details:', error); // Added detailed logging
if (error instanceof ACInfinityClientError) {
throw error;
}
@@ -129,6 +131,9 @@ export class ACInfinityClient {
for (const device of devices) {
const devId = device.devId;
const devName = device.devName || `device-${devId}`;
// Use deviceInfo if available (newer API structure), otherwise fallback to root/devSettings
const info = device.deviceInfo || device;
const settings = device.devSettings || info;
// Normalize device name for use as identifier
const deviceId = devName
@@ -136,52 +141,65 @@ export class ACInfinityClient {
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-|-$/g, '');
// Extract sensor data from device settings or sensor fields
// Temperature is stored as Celsius * 100
if (device.devSettings?.temperature !== undefined) {
// --- Device Level Sensors ---
// Temperature (Celsius * 100)
if (info.temperature !== undefined) {
readings.push({
device: deviceId,
channel: 'temperature',
value: device.devSettings.temperature / 100,
value: info.temperature / 100,
});
} else if (device.temperature !== undefined) {
} else if (settings.temperature !== undefined) {
readings.push({
device: deviceId,
channel: 'temperature',
value: device.temperature / 100,
value: settings.temperature / 100,
});
}
// Humidity is stored as % * 100
if (device.devSettings?.humidity !== undefined) {
// Humidity (% * 100)
if (info.humidity !== undefined) {
readings.push({
device: deviceId,
channel: 'humidity',
value: device.devSettings.humidity / 100,
value: info.humidity / 100,
});
} else if (device.humidity !== undefined) {
} else if (settings.humidity !== undefined) {
readings.push({
device: deviceId,
channel: 'humidity',
value: device.humidity / 100,
value: settings.humidity / 100,
});
}
// VPD if available
if (device.devSettings?.vpdnums !== undefined) {
// VPD
if (info.vpdnums !== undefined) {
readings.push({
device: deviceId,
channel: 'vpd',
value: device.devSettings.vpdnums / 100,
value: info.vpdnums / 100,
});
} else if (settings.vpdnums !== undefined) {
readings.push({
device: deviceId,
channel: 'vpd',
value: settings.vpdnums / 100,
});
}
// Check for port-level sensors (some controllers have multiple ports)
if (device.devPortList && Array.isArray(device.devPortList)) {
for (const port of device.devPortList) {
const portId = port.portId || port.port;
const portDeviceId = `${deviceId}-port${portId}`;
// --- Port Level Sensors/State ---
const ports = info.ports || device.devPortList;
if (ports && Array.isArray(ports)) {
for (const port of ports) {
const portId = port.port || port.portId;
const portName = port.portName || `port${portId}`;
// Create a descriptive suffix for the port device, e.g. "wall-fan" or "wall-port1"
// If portName is generic "Port X", use number. If it's specific "Fan", use that.
const suffix = portName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
const portDeviceId = `${deviceId}-${suffix}`;
// Port specific sensors (if any - sometimes temp usually on device)
if (port.temperature !== undefined) {
readings.push({
device: portDeviceId,
@@ -189,7 +207,6 @@ export class ACInfinityClient {
value: port.temperature / 100,
});
}
if (port.humidity !== undefined) {
readings.push({
device: portDeviceId,
@@ -197,6 +214,15 @@ export class ACInfinityClient {
value: port.humidity / 100,
});
}
// Level / Speed (speak)
if (port.speak !== undefined) {
readings.push({
device: portDeviceId,
channel: 'level',
value: port.speak,
});
}
}
}
}

View File

@@ -21,5 +21,5 @@ export default {
pollIntervalMs: parseInt(process.env.POLL_INTERVAL_MS || '60000', 10),
// AC Infinity API
acApiHost: process.env.AC_API_HOST || 'https://www.acinfinity.com',
acApiHost: process.env.AC_API_HOST || 'http://www.acinfinityserver.com',
};