Tue, 30 Apr 2024 17:26:41 +0200
Version 0.9.19b2. Simulator redesign and it is now possible to run more then one simulator. All simulated devices have address names that include the simulator number. Added the setup screen for the most part. Not compatible with previous versions if a simulator was used, delete all simulators and simulated devices during stop and start.
--- a/configure Sun Apr 28 15:50:42 2024 +0200 +++ b/configure Tue Apr 30 17:26:41 2024 +0200 @@ -2037,7 +2037,7 @@ PACKAGE="mbsePi-apps" -VERSION="0.9.19b1" +VERSION="0.9.19b2" COPYRIGHT="Copyright (C) 2014-2024 Michiel Broek, All Rights Reserved" CYEARS="2014-2024"
--- a/configure.ac Sun Apr 28 15:50:42 2024 +0200 +++ b/configure.ac Tue Apr 30 17:26:41 2024 +0200 @@ -8,7 +8,7 @@ dnl General settings dnl After changeing the version number, run autoconf! PACKAGE="mbsePi-apps" -VERSION="0.9.19b1" +VERSION="0.9.19b2" COPYRIGHT="Copyright (C) 2014-2024 Michiel Broek, All Rights Reserved" CYEARS="2014-2024" AC_SUBST(PACKAGE)
--- a/thermferm/Makefile Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/Makefile Tue Apr 30 17:26:41 2024 +0200 @@ -67,8 +67,8 @@ pid.o: thermferm.h pid.h rc-switch.o: thermferm.h xutil.h delay.h rc-switch.h rdconfig.o: rdconfig.h thermferm.h pid.h futil.h xutil.h -server.o: rdconfig.h thermferm.h delay.h devices.h server.h lcd-buffer.h xutil.h mqtt.h -simulator.o: thermferm.h delay.h simulator.h +server.o: rdconfig.h thermferm.h delay.h one-wire.h devices.h server.h simulator.h lcd-buffer.h xutil.h mqtt.h +simulator.o: thermferm.h delay.h xutil.h websocket.h simulator.h slcd.o: thermferm.h slcd.h futil.h xutil.h thermferm.o: lock.h rdconfig.h server.h thermferm.h devices.h delay.h simulator.h lcd-pcf8574.h lcd-buffer.h slcd.h panel.h one-wire.h futil.h xutil.h pid.h mqtt.h statetbl.h websocket.h websocket.o: thermferm.h xutil.h devices.h websocket.h
--- a/thermferm/devices.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/devices.c Tue Apr 30 17:26:41 2024 +0200 @@ -250,6 +250,9 @@ int rc, i; char buf[40]; #endif +#ifdef USE_SIMULATOR + simulator_list *simulator; +#endif if (uuid == NULL) return 0; @@ -314,22 +317,33 @@ } #ifdef USE_SIMULATOR - if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN) && (device->present == DEVPRESENT_YES)) { - if ((strcmp((char *)"SimCooler", device->address) == 0) || (strcmp((char *)"SimHeater", device->address) == 0) || - (strcmp((char *)"SimFan" , device->address) == 0) || (strcmp((char *)"SimLight" , device->address) == 0)) { - if (value != device->value) { - syslog(LOG_NOTICE, "SIM %s value=%d", device->address, value); - device->value = value; - device->timestamp = time(NULL); - if (strcmp((char *)"SimCooler", device->address) == 0) - SIMcooling = value; - if (strcmp((char *)"SimHeater", device->address) == 0) - SIMheating = value; - if (strcmp((char *)"SimFan", device->address) == 0) - SIMfan = value; - if (strcmp((char *)"SimLight", device->address) == 0) - SIMlight = value; - devices_ws(); + if ((device->type == DEVTYPE_SIM) && (device->direction == DEVDIR_OUT_BIN)) { + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if ((strcmp(device->address, simulator->cooler_address) == 0) || + (strcmp(device->address, simulator->heater_address) == 0) || + (strcmp(device->address, simulator->fan_address) == 0) || + (strcmp(device->address, simulator->light_address) == 0)) { + if (value != device->value) { + int rc = DEVPRESENT_UNDEF; + device->value = value; + device->timestamp = time(NULL); + if (strcmp(device->address, simulator->cooler_address) == 0) { + simulator->cooler_power = value; + rc = simulator->cooler_present; + } else if (strcmp(device->address, simulator->heater_address) == 0) { + simulator->heater_power = value; + rc = simulator->heater_present; + } else if (strcmp(device->address, simulator->fan_address) == 0) { + simulator->fan_power = value; + rc = simulator->fan_present; + } else if (strcmp(device->address, simulator->light_address) == 0) { + simulator->light_power = value; + rc = simulator->light_present; + } + syslog(LOG_NOTICE, "SIM %s value=%d, present=%s", device->address, value, DEVPRESENT[rc]); + device->present = rc; + devices_ws(); + } } } } @@ -462,6 +476,9 @@ int pin; char buf[40]; #endif +#ifdef USE_SIMULATOR + simulator_list *simulator; +#endif /* * Scan for 1-wire devices. These are already detected by the @@ -686,87 +703,110 @@ #endif #ifdef USE_SIMULATOR - found = 0; - for (device = Config.devices; device; device = device->next) { - if (device->type == DEVTYPE_SIM) { - found++; - } - } + + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + for (i = 0; i < 10; i++) { + + found = FALSE; + for (device = Config.devices; device; device = device->next) { + if ((i == 0 && strcmp(device->address, simulator->room_tempaddress) == 0) || + (i == 1 && strcmp(device->address, simulator->air_address) == 0) || + (i == 2 && strcmp(device->address, simulator->beer_address) == 0) || + (i == 3 && strcmp(device->address, simulator->heater_address) == 0) || + (i == 4 && strcmp(device->address, simulator->cooler_address) == 0) || + (i == 5 && strcmp(device->address, simulator->room_humaddress) == 0) || + (i == 6 && strcmp(device->address, simulator->chiller_address) == 0) || + (i == 7 && strcmp(device->address, simulator->fan_address) == 0) || + (i == 8 && strcmp(device->address, simulator->light_address) == 0) || + (i == 9 && strcmp(device->address, simulator->beer_address2) == 0)) { + found = TRUE; + break; + } + } + + if (found == FALSE) { + ndev = (devices_list *)malloc(sizeof(devices_list)); + ndev->next = NULL; + ndev->uuid = malloc(37); + uuid_generate(uu); + uuid_unparse(uu, ndev->uuid); + ndev->type = DEVTYPE_SIM; + ndev->value = ndev->offset = 0; + ndev->present = DEVPRESENT_NO; + ndev->gpiopin = -1; + ndev->subdevice = i; + ndev->comment = xstrcpy((char *)"Auto detected device"); + ndev->timestamp = time(NULL); + ndev->inuse = 0; - /* - * Create simulated devices, or upgrade with new devices. - */ - subdevices = 10; - for (i = found; i < subdevices; i++) { - ndev = (devices_list *)malloc(sizeof(devices_list)); - ndev->next = NULL; - ndev->uuid = malloc(37); - uuid_generate(uu); - uuid_unparse(uu, ndev->uuid); - ndev->type = DEVTYPE_SIM; - ndev->value = ndev->offset = 0; - ndev->present = DEVPRESENT_YES; - ndev->subdevice = i; - ndev->gpiopin = -1; - ndev->comment = xstrcpy((char *)"Auto detected device"); - ndev->timestamp = time(NULL); - ndev->inuse = 0; - switch (i) { - case 0: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimRoomTemp"); - ndev->description = xstrcpy((char *)"Simulated room temperature"); - break; - case 1: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimAirTemp"); - ndev->description = xstrcpy((char *)"Simulated air temperature"); - break; - case 2: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimBeerTemp"); - ndev->description = xstrcpy((char *)"Simulated beer temperature"); - break; - case 3: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimHeater"); - ndev->description = xstrcpy((char *)"Simulated heater"); - break; - case 4: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimCooler"); - ndev->description = xstrcpy((char *)"Simulated cooler"); - break; - case 5: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimRoomHum"); - ndev->description = xstrcpy((char *)"Simulated room humidity"); - break; - case 6: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimChillerTemp"); - ndev->description = xstrcpy((char *)"Simulated Chiller temperature"); - break; - case 7: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimFan"); - ndev->description = xstrcpy((char *)"Simulated fan"); - break; - case 8: ndev->direction = DEVDIR_OUT_BIN; - ndev->address = xstrcpy((char *)"SimLight"); - ndev->description = xstrcpy((char *)"Simulated light"); - break; - case 9: ndev->direction = DEVDIR_IN_ANALOG; - ndev->address = xstrcpy((char *)"SimBeerTemp2"); - ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)"); - break; - } + switch (i) { + case 0: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->room_tempaddress); + ndev->description = xstrcpy((char *)"Simulated room temperature"); + ndev->present = DEVPRESENT_YES; + break; + case 1: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->air_address); + ndev->description = xstrcpy((char *)"Simulated air temperature"); + ndev->present = simulator->air_present; + break; + case 2: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->beer_address); + ndev->description = xstrcpy((char *)"Simulated beer temperature"); + ndev->present = simulator->beer_present; + break; + case 3: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->heater_address); + ndev->description = xstrcpy((char *)"Simulated heater"); + ndev->present = simulator->heater_present; + break; + case 4: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->cooler_address); + ndev->description = xstrcpy((char *)"Simulated cooler"); + ndev->present = simulator->cooler_present; + break; + case 5: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->room_humaddress); + ndev->description = xstrcpy((char *)"Simulated room humidity"); + ndev->present = DEVPRESENT_YES; + break; + case 6: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->chiller_address); + ndev->description = xstrcpy((char *)"Simulated Chiller temperature"); + ndev->present = simulator->chiller_present; + break; + case 7: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->fan_address); + ndev->description = xstrcpy((char *)"Simulated fan"); + ndev->present = simulator->fan_present; + break; + case 8: ndev->direction = DEVDIR_OUT_BIN; + ndev->address = xstrcpy(simulator->light_address); + ndev->description = xstrcpy((char *)"Simulated light"); + ndev->present = simulator->light_present; + break; + case 9: ndev->direction = DEVDIR_IN_ANALOG; + ndev->address = xstrcpy(simulator->beer_address2); + ndev->description = xstrcpy((char *)"Simulated beer temperature (alt)"); + ndev->present = simulator->beer_present2; + break; + } - syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d, %s", ndev->address, ndev->subdevice, ndev->description); + syslog(LOG_NOTICE, "New Simulator device %s, subdevice %d", ndev->address, ndev->subdevice); - if (Config.devices == NULL) { - Config.devices = ndev; - } else { - for (device = Config.devices; device; device = device->next) { - if (device->next == NULL) { - device->next = ndev; - break; + if (Config.devices == NULL) { + Config.devices = ndev; + } else { + for (device = Config.devices; device; device = device->next) { + if (device->next == NULL) { + device->next = ndev; + break; + } + } } + rc++; } } - rc++; } #endif
--- a/thermferm/rdconfig.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/rdconfig.c Tue Apr 30 17:26:41 2024 +0200 @@ -154,6 +154,26 @@ free(simulator->uuid); if (simulator->name) free(simulator->name); + if (simulator->room_tempaddress) + free(simulator->room_tempaddress); + if (simulator->room_humaddress) + free(simulator->room_humaddress); + if (simulator->air_address) + free(simulator->air_address); + if (simulator->beer_address) + free(simulator->beer_address); + if (simulator->beer_address2) + free(simulator->beer_address2); + if (simulator->chiller_address) + free(simulator->chiller_address); + if (simulator->cooler_address) + free(simulator->cooler_address); + if (simulator->heater_address) + free(simulator->heater_address); + if (simulator->fan_address) + free(simulator->fan_address); + if (simulator->light_address) + free(simulator->light_address); free(simulator); } Config.simulators = NULL; @@ -430,22 +450,45 @@ xmlTextWriterStartElement(writer, BAD_CAST "SIMULATOR"); xmlTextWriterWriteFormatElement(writer, BAD_CAST "UUID", "%s", simulator->uuid); xmlTextWriterWriteFormatElement(writer, BAD_CAST "NAME", "%s", simulator->name); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "SIMNO", "%d", simulator->simno); xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_AIR", "%d", simulator->volume_air); xmlTextWriterWriteFormatElement(writer, BAD_CAST "VOLUME_BEER", "%d", simulator->volume_beer); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPADDRESS", "%s", simulator->room_tempaddress); xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_TEMPERATURE", "%.1f", simulator->room_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_HUMADDRESS", "%s", simulator->room_humaddress); xmlTextWriterWriteFormatElement(writer, BAD_CAST "ROOM_HUMIDITY", "%.1f", simulator->room_humidity); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_ADDRESS", "%s", simulator->air_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_TEMPERATURE", "%f", simulator->air_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "AIR_PRESENT", "%s", DEVPRESENT[simulator->air_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_ADDRESS", "%s", simulator->beer_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE", "%f", simulator->beer_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_PRESENT", "%s", DEVPRESENT[simulator->beer_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_ADDRESS2", "%s", simulator->beer_address2); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_TEMPERATURE2", "%f", simulator->beer_temperature2); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "BEER_PRESENT2", "%s", DEVPRESENT[simulator->beer_present2]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_ADDRESS", "%s", simulator->chiller_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_TEMPERATURE", "%f", simulator->chiller_temperature); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "CHILLER_PRESENT", "%s", DEVPRESENT[simulator->chiller_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_ADDRESS", "%s", simulator->cooler_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TEMP", "%f", simulator->cooler_temp); xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_TIME", "%d", simulator->cooler_time); xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_SIZE", "%.3f", simulator->cooler_size); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_PRESENT", "%s", DEVPRESENT[simulator->cooler_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_POWER", "%d", simulator->cooler_power); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_ADDRESS", "%s", simulator->heater_address); xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TEMP", "%f", simulator->heater_temp); xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_TIME", "%d", simulator->heater_time); xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_SIZE", "%.3f", simulator->heater_size); - xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_STATE", "%d", simulator->heater_state); - xmlTextWriterWriteFormatElement(writer, BAD_CAST "COOLER_STATE", "%d", simulator->cooler_state); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_PRESENT", "%s", DEVPRESENT[simulator->heater_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "HEATER_POWER", "%d", simulator->heater_power); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_ADDRESS", "%s", simulator->fan_address); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_PRESENT", "%s", DEVPRESENT[simulator->fan_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "FAN_POWER", "%d", simulator->fan_power); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_ADDRESS", "%s", simulator->light_address); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_PRESENT", "%s", DEVPRESENT[simulator->light_present]); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "LIGHT_POWER", "%d", simulator->light_power); xmlTextWriterWriteFormatElement(writer, BAD_CAST "FRIGO_ISOLATION", "%.3f", simulator->frigo_isolation); + xmlTextWriterWriteFormatElement(writer, BAD_CAST "TIMESTAMP", "%ld", (long)simulator->timestamp); xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_HEAT", "%f", simulator->s_yeast_heat); xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_YEAST_STARTED", "%d", (int)simulator->s_yeast_started); xmlTextWriterWriteFormatElement(writer, BAD_CAST "S_COOL_TEMP", "%f", simulator->s_cool_temp); @@ -1328,18 +1371,22 @@ { xmlChar *key; simulator_list *simulator, *tmp; - int ival; + int ival, i; float fval; simulator = (simulator_list *)malloc(sizeof(simulator_list)); simulator->next = NULL; simulator->uuid = simulator->name = NULL; - simulator->volume_air = simulator->volume_beer = 0; + simulator->room_tempaddress = simulator->room_humaddress = simulator->air_address = simulator->beer_address = simulator->beer_address2 = NULL; + simulator->chiller_address = simulator->cooler_address = simulator->heater_address = simulator->fan_address = simulator->light_address = NULL; + simulator->simno = simulator->volume_air = simulator->volume_beer = 0; simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0; simulator->chiller_temperature = 1.5; simulator->room_humidity = 49.2; simulator->cooler_temp = simulator->cooler_size = simulator->heater_temp = simulator->heater_size = simulator->frigo_isolation = 0.0; - simulator->cooler_time = simulator->heater_time = simulator->cooler_state = simulator->heater_state = 0; + simulator->cooler_time = simulator->heater_time = 0; + simulator->air_present = simulator->beer_present = DEVPRESENT_YES; + simulator->beer_present2 = simulator->chiller_present = simulator->cooler_present = simulator->heater_present = DEVPRESENT_UNDEF; simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (time_t)0; simulator->s_yeast_heat = simulator->s_cool_temp = simulator->s_heat_temp = 0.0; @@ -1351,6 +1398,12 @@ if ((!xmlStrcmp(cur->name, (const xmlChar *)"NAME"))) { simulator->name = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"SIMNO"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->simno = ival; + xmlFree(key); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"VOLUME_AIR"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%d", &ival) == 1) @@ -1363,36 +1416,103 @@ simulator->volume_beer = ival; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPADDRESS"))) { + simulator->room_tempaddress = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_TEMPERATURE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->room_temperature = fval; xmlFree(key); } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_GUMIDITY"))) { + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_HUMADDRESS"))) { + simulator->room_humaddress = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"ROOM_HUMIDITY"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->room_humidity= fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_ADDRESS"))) { + simulator->air_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_TEMPERATURE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->air_temperature = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"AIR_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->air_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_ADDRESS"))) { + simulator->beer_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->beer_temperature = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->beer_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_ADDRESS2"))) { + simulator->beer_address2 = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_TEMPERATURE2"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%f", &fval) == 1) + simulator->beer_temperature2 = fval; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"BEER_PRESENT2"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->beer_present2 = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_ADDRESS"))) { + simulator->chiller_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_TEMPERATURE"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->chiller_temperature = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"CHILLER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->chiller_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_ADDRESS"))) { + simulator->cooler_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_TEMP"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) @@ -1411,6 +1531,25 @@ simulator->cooler_size = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->cooler_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->cooler_power = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_ADDRESS"))) { + simulator->heater_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_TEMP"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) @@ -1429,24 +1568,72 @@ simulator->heater_size = fval; xmlFree(key); } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_STATE"))) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - if (sscanf((const char *)key, "%d", &ival) == 1) - simulator->heater_state = ival; - xmlFree(key); - } - if ((!xmlStrcmp(cur->name, (const xmlChar *)"COOLER_STATE"))) { - key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); - if (sscanf((const char *)key, "%d", &ival) == 1) - simulator->cooler_state = ival; - xmlFree(key); - } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->heater_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"HEATER_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->heater_power = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_ADDRESS"))) { + simulator->fan_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->fan_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"FAN_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->fan_power = ival; + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_ADDRESS"))) { + simulator->light_address = (char *)xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_PRESENT"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + for (i = 0; i < 4; i++) { + if (! xmlStrcmp(key, (const xmlChar *)DEVPRESENT[i])) { + simulator->light_present = i; + break; + } + } + xmlFree(key); + } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"LIGHT_POWER"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->light_power = ival; + xmlFree(key); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"FRIGO_ISOLATION"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); if (sscanf((const char *)key, "%f", &fval) == 1) simulator->frigo_isolation = fval; xmlFree(key); } + if ((!xmlStrcmp(cur->name, (const xmlChar *)"TIMESTAMP"))) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + if (sscanf((const char *)key, "%d", &ival) == 1) + simulator->timestamp = ival; + xmlFree(key); + } if ((!xmlStrcmp(cur->name, (const xmlChar *)"S_YEAST_HEAT"))) { key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
--- a/thermferm/server.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/server.c Tue Apr 30 17:26:41 2024 +0200 @@ -26,6 +26,7 @@ #include "one-wire.h" #include "devices.h" #include "server.h" +#include "simulator.h" #include "lcd-buffer.h" #include "xutil.h" #include "mqtt.h" @@ -1003,7 +1004,7 @@ { char *opt, *param, *kwd, *val, ibuf[SS_BUFSIZE]; simulator_list *simulator, *tmps; - int rc, rlen, ival; + int rc, rlen, ival, i; float fval; uuid_t uu; @@ -1024,6 +1025,7 @@ srv_send(s, (char *)"SIMULATOR LIST List all Simulators"); srv_send(s, (char *)"SIMULATOR GET uuid Get Simulator record by uuid"); srv_send(s, (char *)"SIMULATOR PUT uuid Put Simulator record by uuid"); + srv_send(s, (char *)"SIMULATOR JSON Get json records"); srv_send(s, (char *)"."); return 0; } @@ -1031,23 +1033,57 @@ if (strcmp(opt, (char *)"LIST") == 0) { srv_send(s, (char *)"212 Simulators list follows:"); for (simulator = Config.simulators; simulator; simulator = simulator->next) { - srv_send(s, (char *)"%s,%s", simulator->uuid, simulator->name); + srv_send(s, (char *)"%s,%d,%s", simulator->uuid, simulator->simno, simulator->name); } srv_send(s, (char *)"."); return 0; } + if (strcmp(opt, (char *)"JSON") == 0) { + char *payload = NULL, *payloadu = NULL; + bool comma = false; + + if (param == NULL) { + srv_send(s, (char *)"212 Simulators json list follows:"); + payload = xstrcpy((char *)"["); + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (comma) + payload = xstrcat(payload, (char *)","); + payloadu = simulator_json(simulator); + payload = xstrcat(payload, payloadu); + comma = true; + free(payloadu); + payloadu = NULL; + } + payload = xstrcat(payload, (char *)"]"); + large_send(s, payload); + srv_send(s, (char *)"."); + free(payload); + payload = NULL; + return 0; + } + } + if (param == NULL) { srv_send(s, (char *)"502 Parameter missing"); return 0; } if (strcmp(opt, (char *)"ADD") == 0) { + int highno = 0, count = 0; + char abuf[64]; /* * For now, only one simulator is allowed. */ if (Config.simulators) { + for (tmps = Config.simulators; tmps; tmps = tmps->next) { + if (tmps->simno > highno) + highno = tmps->simno; + count++; + } + } + if (count >= 5) { srv_send(s, (char *)"441 Maximum simulators reached"); return 0; } @@ -1058,10 +1094,33 @@ uuid_generate(uu); uuid_unparse(uu, simulator->uuid); simulator->name = xstrcpy(param); + simulator->simno = highno + 1; + sprintf(abuf, "%d-", simulator->simno); simulator->volume_air = 150; simulator->volume_beer = 50; - simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->s_cool_temp = simulator->s_heat_temp = 20.0; + simulator->room_tempaddress = xstrcpy(abuf); + simulator->room_tempaddress = xstrcat(simulator->room_tempaddress, (char *)"SimRoomTemp"); + simulator->room_temperature = simulator->air_temperature = simulator->beer_temperature = simulator->beer_temperature2 = 20.0; + simulator->s_cool_temp = simulator->s_heat_temp = 20.0; + simulator->room_humaddress = xstrcpy(abuf); + simulator->room_humaddress = xstrcat(simulator->room_humaddress, (char *)"SimRoomHum"); simulator->room_humidity = 48.6; + simulator->air_address = xstrcpy(abuf); + simulator->air_address = xstrcat(simulator->air_address, (char *)"SimAirTemp"); + simulator->beer_address = xstrcpy(abuf); + simulator->beer_address = xstrcat(simulator->beer_address, (char *)"SimBeerTemp"); + simulator->beer_address2 = xstrcpy(abuf); + simulator->beer_address2 = xstrcat(simulator->beer_address2, (char *)"SimBeerTemp2"); + simulator->chiller_address = xstrcpy(abuf); + simulator->chiller_address = xstrcat(simulator->chiller_address, (char *)"SimChillerTemp"); + simulator->heater_address = xstrcpy(abuf); + simulator->heater_address = xstrcat(simulator->heater_address, (char *)"SimHeater"); + simulator->cooler_address = xstrcpy(abuf); + simulator->cooler_address = xstrcat(simulator->cooler_address, (char *)"SimCooler"); + simulator->fan_address = xstrcpy(abuf); + simulator->fan_address = xstrcat(simulator->fan_address, (char *)"SimFan"); + simulator->light_address = xstrcpy(abuf); + simulator->light_address = xstrcat(simulator->light_address, (char *)"SimLight"); simulator->chiller_temperature = 1.5; /* Chiller temperature */ simulator->cooler_temp = 1.5; /* Cooling temperature */ simulator->cooler_time = 720; /* About 12 minutes for the cooler plate */ @@ -1069,8 +1128,10 @@ simulator->heater_temp = 150.0; /* Heating temperature */ simulator->heater_time = 3; /* 3 seconds to heat-up */ simulator->heater_size = 0.01; /* 0.01 square meter heater plate */ - simulator->heater_state = simulator->cooler_state = 0; + simulator->air_present = simulator->beer_present = DEVPRESENT_YES; + simulator->beer_present2 = simulator->chiller_present = simulator->cooler_present = simulator->heater_present = DEVPRESENT_UNDEF; simulator->frigo_isolation = 0.002; + simulator->timestamp = time(NULL); simulator->s_yeast_heat = 0.0; simulator->s_yeast_started = simulator->s_cool_changed = simulator->s_heat_changed = (int)0; @@ -1085,12 +1146,14 @@ } } - syslog(LOG_NOTICE, "Simulator %s added", simulator->uuid); + syslog(LOG_NOTICE, "Simulator %s no %d added", simulator->uuid, simulator->simno); srv_send(s, (char *)"211 Simulator %s added", simulator->uuid); return 1; } if (strcmp(opt, (char *)"DEL") == 0) { + // TODO: check devices in use. + // TODO: delete simulated devices. rc = delete_Simulator(param); if (rc) { syslog(LOG_NOTICE, "Simulator %s deleted", param); @@ -1107,22 +1170,45 @@ if (strcmp(simulator->uuid, param) == 0) { srv_send(s, (char *)"213 Simulator record follows:"); srv_send(s, (char *)"NAME,%s", simulator->name); + srv_send(s, (char *)"SIMNO,%d", simulator->simno); srv_send(s, (char *)"VOLUME_AIR,%d", simulator->volume_air); srv_send(s, (char *)"VOLUME_BEER,%d", simulator->volume_beer); + srv_send(s, (char *)"ROOM_TEMPADDRESS,%s", simulator->room_tempaddress); srv_send(s, (char *)"ROOM_TEMPERATURE,%.1f", simulator->room_temperature); + srv_send(s, (char *)"ROOM_HUMADDRESS,%s", simulator->room_humaddress); srv_send(s, (char *)"ROOM_HUMIDITY,%.1f", simulator->room_humidity); + srv_send(s, (char *)"AIR_ADDRESS,%s", simulator->air_address); srv_send(s, (char *)"AIR_TEMPERATURE,%.3f", simulator->air_temperature); + srv_send(s, (char *)"AIR_PRESENT,%s", DEVPRESENT[simulator->air_present]); + srv_send(s, (char *)"BEER_ADDRESS,%s", simulator->beer_address); srv_send(s, (char *)"BEER_TEMPERATURE,%.3f", simulator->beer_temperature); + srv_send(s, (char *)"BEER_PRESENT,%s", DEVPRESENT[simulator->beer_present]); + srv_send(s, (char *)"BEER_ADDRESS2,%s", simulator->beer_address2); + srv_send(s, (char *)"BEER_TEMPERATURE2,%.3f", simulator->beer_temperature2); + srv_send(s, (char *)"BEER_PRESENT2,%s", DEVPRESENT[simulator->beer_present2]); + srv_send(s, (char *)"CHILLER_ADDRESS,%s", simulator->chiller_address); srv_send(s, (char *)"CHILLER_TEMPERATURE,%.3f", simulator->chiller_temperature); + srv_send(s, (char *)"CHILLER_PRESENT,%s", DEVPRESENT[simulator->chiller_present]); + srv_send(s, (char *)"COOLER_ADDRESS,%s", simulator->cooler_address); srv_send(s, (char *)"COOLER_TEMP,%.1f", simulator->cooler_temp); srv_send(s, (char *)"COOLER_TIME,%d", simulator->cooler_time); srv_send(s, (char *)"COOLER_SIZE,%.3f", simulator->cooler_size); + srv_send(s, (char *)"COOLER_PRESENT,%s", DEVPRESENT[simulator->cooler_present]); + srv_send(s, (char *)"COOLER_POWER,%d", simulator->cooler_power); + srv_send(s, (char *)"HEATER_ADDRESS,%s", simulator->heater_address); srv_send(s, (char *)"HEATER_TEMP,%.1f", simulator->heater_temp); srv_send(s, (char *)"HEATER_TIME,%d", simulator->heater_time); srv_send(s, (char *)"HEATER_SIZE,%.3f", simulator->heater_size); - srv_send(s, (char *)"HEATER_STATE,%d", simulator->heater_state); - srv_send(s, (char *)"COOLER_STATE,%d", simulator->cooler_state); + srv_send(s, (char *)"HEATER_PRESENT,%s", DEVPRESENT[simulator->heater_present]); + srv_send(s, (char *)"HEATER_POWER,%d", simulator->heater_power); + srv_send(s, (char *)"FAN_ADDRESS,%s", simulator->fan_address); + srv_send(s, (char *)"FAN_PRESENT,%s", DEVPRESENT[simulator->fan_present]); + srv_send(s, (char *)"FAN_POWER,%d", simulator->fan_power); + srv_send(s, (char *)"LIGHT_ADDRESS,%s", simulator->light_address); + srv_send(s, (char *)"LIGHT_PRESENT,%s", DEVPRESENT[simulator->light_present]); + srv_send(s, (char *)"LIGHT_POWER,%d", simulator->light_power); srv_send(s, (char *)"FRIGO_ISOLATION,%.3f", simulator->frigo_isolation); + srv_send(s, (char *)"TIMESTAMP,%ld", (long)simulator->timestamp); srv_send(s, (char *)"."); return 0; } @@ -1142,6 +1228,7 @@ if (strlen(ibuf)) { if (strcmp(ibuf, (char *)".") == 0) { srv_send(s, (char *)"219 Accepted Simulator record"); + simulator->timestamp = time(NULL); return 1; } kwd = strtok(ibuf, ",\0"); @@ -1191,6 +1278,16 @@ simulator->air_temperature = fval; } + } else if (strcmp(kwd, (char *)"AIR_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->air_present != i) + syslog(LOG_NOTICE, "Simulator %s air_present %s to %s", simulator->uuid, DEVPRESENT[simulator->air_present], DEVPRESENT[i]); + simulator->air_present = i; + break; + } + } + } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->beer_temperature != fval) @@ -1198,6 +1295,33 @@ simulator->beer_temperature = fval; } + } else if (strcmp(kwd, (char *)"BEER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->beer_present != i) + syslog(LOG_NOTICE, "Simulator %s beer_present %s to %s", simulator->uuid, DEVPRESENT[simulator->beer_present], DEVPRESENT[i]); + simulator->beer_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"BEER_TEMPERATURE2") == 0) { + if (sscanf(val, "%f", &fval) == 1) { + if (simulator->beer_temperature2 != fval) + syslog(LOG_NOTICE, "Simulator %s beer temperature2 %.1f to %.1f", simulator->uuid, simulator->beer_temperature2, fval); + simulator->beer_temperature2 = fval; + } + + } else if (strcmp(kwd, (char *)"BEER_PRESENT2") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->beer_present2 != i) + syslog(LOG_NOTICE, "Simulator %s beer_present2 %s to %s", simulator->uuid, DEVPRESENT[simulator->beer_present2], DEVPRESENT[i]); + simulator->beer_present2 = i; + break; + } + } + } else if (strcmp(kwd, (char *)"CHILLER_TEMPERATURE") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->chiller_temperature != fval) @@ -1205,6 +1329,16 @@ simulator->chiller_temperature = fval; } + } else if (strcmp(kwd, (char *)"CHILLER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->chiller_present != i) + syslog(LOG_NOTICE, "Simulator %s chiller_present %s to %s", simulator->uuid, DEVPRESENT[simulator->chiller_present], DEVPRESENT[i]); + simulator->chiller_present = i; + break; + } + } + } else if (strcmp(kwd, (char *)"COOLER_TEMP") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->cooler_temp != fval) @@ -1226,6 +1360,23 @@ simulator->cooler_size = fval; } + } else if (strcmp(kwd, (char *)"COOLER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->cooler_present != i) + syslog(LOG_NOTICE, "Simulator %s cooler_present %s to %s", simulator->uuid, DEVPRESENT[simulator->cooler_present], DEVPRESENT[i]); + simulator->cooler_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"COOLER_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->cooler_power != ival) + syslog(LOG_NOTICE, "Simulator %s cooler power %d to %d", simulator->uuid, simulator->cooler_power, ival); + simulator->cooler_power = ival; + } + } else if (strcmp(kwd, (char *)"HEATER_TEMP") == 0) { if (sscanf(val, "%f", &fval) == 1) { if (simulator->heater_temp != fval) @@ -1247,19 +1398,56 @@ simulator->heater_size = fval; } - } else if (strcmp(kwd, (char *)"HEATER_STATE") == 0) { - if (sscanf(val, "%d", &ival) == 1) { - if (simulator->heater_state != ival) - syslog(LOG_NOTICE, "Simulator %s heater state %d to %d", simulator->uuid, simulator->heater_state, ival); - simulator->heater_state = ival; - } + } else if (strcmp(kwd, (char *)"HEATER_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->heater_present != i) + syslog(LOG_NOTICE, "Simulator %s heater_present %s to %s", simulator->uuid, DEVPRESENT[simulator->heater_present], DEVPRESENT[i]); + simulator->heater_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"HEATER_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->heater_power != ival) + syslog(LOG_NOTICE, "Simulator %s heater power %d to %d", simulator->uuid, simulator->heater_power, ival); + simulator->heater_power = ival; + } - } else if (strcmp(kwd, (char *)"COOLER_STATE") == 0) { - if (sscanf(val, "%d", &ival) == 1) { - if (simulator->cooler_state != ival) - syslog(LOG_NOTICE, "Simulator %s cooler state %d to %d", simulator->uuid, simulator->cooler_state, ival); - simulator->cooler_state = ival; - } + } else if (strcmp(kwd, (char *)"FAN_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->fan_present != i) + syslog(LOG_NOTICE, "Simulator %s fan_present %s to %s", simulator->uuid, DEVPRESENT[simulator->fan_present], DEVPRESENT[i]); + simulator->fan_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"FAN_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->fan_power != ival) + syslog(LOG_NOTICE, "Simulator %s fan power %d to %d", simulator->uuid, simulator->fan_power, ival); + simulator->fan_power = ival; + } + + } else if (strcmp(kwd, (char *)"LIGHT_PRESENT") == 0) { + for (i = 0; i < 4; i++) { + if (strcmp(val, DEVPRESENT[i]) == 0) { + if (simulator->light_present != i) + syslog(LOG_NOTICE, "Simulator %s light_present %s to %s", simulator->uuid, DEVPRESENT[simulator->light_present], DEVPRESENT[i]); + simulator->light_present = i; + break; + } + } + + } else if (strcmp(kwd, (char *)"LIGHT_POWER") == 0) { + if (sscanf(val, "%d", &ival) == 1) { + if (simulator->fan_power != ival) + syslog(LOG_NOTICE, "Simulator %s light power %d to %d", simulator->uuid, simulator->light_power, ival); + simulator->light_power = ival; + } } else if (strcmp(kwd, (char *)"FRIGO_ISOLATION") == 0) { if (sscanf(val, "%f", &fval) == 1) {
--- a/thermferm/simulator.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/simulator.c Tue Apr 30 17:26:41 2024 +0200 @@ -22,6 +22,8 @@ #include "thermferm.h" #include "delay.h" +#include "xutil.h" +#include "websocket.h" #include "simulator.h" int my_simulator_state = 0; @@ -29,6 +31,7 @@ #ifdef USE_SIMULATOR extern sys_config Config; +extern const char DEVPRESENT[4][6]; int my_simulator_shutdown = 0; int SIMcooling = 0; @@ -37,6 +40,157 @@ int SIMlight = 0; +/* + * Return json data for one simulator + */ +char *simulator_json(simulator_list *simulator) +{ + char *payload, buf[64]; + + payload = xstrcpy((char *)"{\"uuid\":\""); + payload = xstrcat(payload, simulator->uuid); + payload = xstrcat(payload, (char *)"\",\"name\":\""); + payload = xstrcat(payload, simulator->name); + payload = xstrcat(payload, (char *)"\",\"simno\":"); + sprintf(buf, "%d", simulator->simno); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"volume_air\":"); + sprintf(buf, "%d", simulator->volume_air); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"volume_beer\":"); + sprintf(buf, "%d", simulator->volume_beer); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"room\":{\"tempaddress\":\""); + payload = xstrcat(payload, simulator->room_tempaddress); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.1f", simulator->room_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"humaddress\":\""); + payload = xstrcat(payload, simulator->room_humaddress); + payload = xstrcat(payload, (char *)"\",\"humidity\":"); + sprintf(buf, "%.1f", simulator->room_humidity); + payload = xstrcat(payload, buf); + + payload = xstrcat(payload, (char *)"},\"air\":{\"address\":\""); + payload = xstrcat(payload, simulator->air_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->air_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->air_present]); + payload = xstrcat(payload, (char *)"\"},\"beer\":{\"address\":\""); + + payload = xstrcat(payload, simulator->beer_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->beer_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->beer_present]); + payload = xstrcat(payload, (char *)"\"},\"beer2\":{\"address\":\""); + + payload = xstrcat(payload, simulator->beer_address2); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->beer_temperature2); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->beer_present2]); + payload = xstrcat(payload, (char *)"\"},\"chiller\":{\"address\":\""); + + payload = xstrcat(payload, simulator->chiller_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->chiller_temperature); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->chiller_present]); + payload = xstrcat(payload, (char *)"\"},\"cooler\":{\"address\":\""); + + payload = xstrcat(payload, simulator->cooler_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->cooler_temp); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"time\":"); + sprintf(buf, "%d", simulator->cooler_time); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"size\":"); + sprintf(buf, "%.4f", simulator->cooler_size); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->cooler_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->cooler_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"heater\":{\"address\":\""); + + payload = xstrcat(payload, simulator->heater_address); + payload = xstrcat(payload, (char *)"\",\"temperature\":"); + sprintf(buf, "%.4f", simulator->heater_temp); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"time\":"); + sprintf(buf, "%d", simulator->heater_time); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"size\":"); + sprintf(buf, "%.4f", simulator->heater_size); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->heater_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->heater_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"fan\":{\"address\":\""); + + payload = xstrcat(payload, simulator->fan_address); + payload = xstrcat(payload, (char *)"\",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->fan_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->fan_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"light\":{\"address\":\""); + + payload = xstrcat(payload, simulator->light_address); + payload = xstrcat(payload, (char *)"\",\"present\":\""); + payload = xstrcat(payload, (char *)DEVPRESENT[simulator->light_present]); + payload = xstrcat(payload, (char *)"\",\"power\":"); + sprintf(buf, "%d", simulator->light_power); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"},\"frigo_isolation\":"); + + sprintf(buf, "%.4f", simulator->frigo_isolation); + payload = xstrcat(payload, buf); + + payload = xstrcat(payload, (char *)",\"timestamp\":"); + sprintf(buf, "%ld", (long)simulator->timestamp); + payload = xstrcat(payload, buf); + payload = xstrcat(payload, (char *)"}"); + + return payload; +} + + + +void simulator_ws(void) +{ + bool comma = false; + char *payload = NULL, *payloadu = NULL; + simulator_list *simulator; + + payload = xstrcpy((char *)"{\"type\":\"simulator\",\"metric\":["); + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (comma) + payload = xstrcat(payload, (char *)","); + payloadu = simulator_json(simulator); + payload = xstrcat(payload, payloadu); + comma = true; + free(payloadu); + payloadu = NULL; + } + payload = xstrcat(payload, (char *)"]}"); + ws_broadcast(payload); + free(payload); + payload = NULL; +} + + + void *my_simulator_loop(void *threadid) { simulator_list *simulator; @@ -59,17 +213,17 @@ if (my_simulator_shutdown) break; - for (simulator = Config.simulators; simulator; simulator = simulator->next) { - if (my_simulator_shutdown) - break; + now = time(NULL); + if (now != last) { + last = now; + /* + * Each second + */ + seconds++; - now = time(NULL); - if (now != last) { - last = now; - /* - * Each second - */ - seconds++; + for (simulator = Config.simulators; simulator; simulator = simulator->next) { + if (my_simulator_shutdown) + break; /* * First, calculate temperature difference between the room and the air in the @@ -79,7 +233,7 @@ */ sqm_room_air = (cbrtl(simulator->volume_air) * cbrtl(simulator->volume_air) * 6) / 100; /* square meters all fridge sides */ thick_room_air = 0.04; /* 4 cm walls */ - k_room_air = 0.03; /* Polystrene */ + k_room_air = 0.03; /* Polystrene */ air_heat_transfer=(k_room_air * sqm_room_air * (simulator->room_temperature - simulator->air_temperature)) / thick_room_air; air_change = (air_heat_transfer / (vhc_air * ((simulator->volume_air - simulator->volume_beer) * 1000))) / 60.0; simulator->air_temperature += air_change; @@ -117,7 +271,7 @@ simulator->s_cool_temp -= (simulator->s_cool_temp - simulator->air_temperature) / 25.0; } - /* + /* * Calculate final temperature of the beer and the air. */ // Cheap trick, just follow slowly the air temp.
--- a/thermferm/simulator.h Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/simulator.h Tue Apr 30 17:26:41 2024 +0200 @@ -3,12 +3,10 @@ #ifdef USE_SIMULATOR +char *simulator_json(simulator_list *simulator); +void simulator_ws(void); -#ifdef HAVE_WIRINGPI_H -PI_THREAD (my_simulator_loop); -#else void *my_simulator_loop(void *); -#endif #endif
--- a/thermferm/thermferm.c Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/thermferm.c Tue Apr 30 17:26:41 2024 +0200 @@ -1412,7 +1412,7 @@ if ((unit->mode == UNITMODE_BEER) && ((unit->air_temperature / 1000.0) > (unit->PID_heat->Input + 8.0))) { unit->PID_heat->OutP = 0.0; } - if (seconds == 60) { + if ((seconds == 60) && (unit->mode > UNITMODE_NONE)) { syslog(LOG_NOTICE, "Heat: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f", unit->PID_heat->SetP, unit->PID_heat->Input, unit->PID_heat->iState, unit->PID_heat->Err, unit->PID_heat->OutP); } @@ -1438,7 +1438,7 @@ } } } - if (seconds == 60) { + if ((seconds == 60) && (unit->mode > UNITMODE_NONE)) { syslog(LOG_NOTICE, "Cool: sp=%.3f Input=%.3f iState=%.3f Err=%.3f Out=%.1f", unit->PID_cool->SetP, unit->PID_cool->Input, unit->PID_cool->iState, unit->PID_cool->Err, unit->PID_cool->OutP); }
--- a/thermferm/thermferm.h Sun Apr 28 15:50:42 2024 +0200 +++ b/thermferm/thermferm.h Tue Apr 30 17:26:41 2024 +0200 @@ -330,22 +330,46 @@ struct _simulator *next; char *uuid; /* Simulator uuid */ char *name; /* Simulator name */ + int simno; /* Simulator number */ int volume_air; /* Volume air of the frigo */ int volume_beer; /* Volume beer inside frigo */ + char *room_tempaddress; /* Address */ double room_temperature; /* Temp outside frigo */ + char *room_humaddress; /* Address */ double room_humidity; /* Humidity outside frigo */ + char *air_address; /* Simulated air address */ double air_temperature; /* Simulated air temperature */ + int air_present; /* Simulated air present */ + char *beer_address; double beer_temperature; /* Simulated beer temperature */ + int beer_present; /* Simulated beer present */ + char *beer_address2; + double beer_temperature2; /* Simulated beer temperature2 */ + int beer_present2; /* Simulated beer present2 */ + char *chiller_address; double chiller_temperature; /* Simulated chiller temp. */ + int chiller_present; /* Simulated chiller present */ + char *cooler_address; double cooler_temp; /* Lowest cooler temperature */ int cooler_time; /* Time to reach temperature */ float cooler_size; /* Size of cooler in square mtr */ + int cooler_present; /* Simulated cooler present */ + int cooler_power; /* Simulated cooler 0..100% */ + char *heater_address; double heater_temp; /* Highest heater temperature */ int heater_time; /* Time to reach temperature */ float heater_size; /* Size of heater in square mtr */ - int heater_state; /* Heater status */ - int cooler_state; /* Cooler status */ + int heater_present; /* Simulated heater present */ + int heater_power; /* Simulated heater 0..100% */ + char *fan_address; /* Simulated fan */ + int fan_present; + int fan_power; + char *light_address; /* Simulated interior light */ + int light_present; + int light_power; float frigo_isolation; /* Frigo isolation value */ + time_t timestamp; + /* * Status values, maintained by the simulator but stored * here so they don't get lost over program restarts.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/dbdevices.php Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,112 @@ +<?php + + +function open_socket() +{ + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + + if (!($sock === false)) { + if (socket_connect($sock, "localhost", 6554)) { + socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 15, 'usec' => 0)); + } else { + socket_close($sock); + } + } + return $sock; +} + + +/** + * @param string $command to send to the server. + * @return string with the complete reply from the + * server. This can be a multiline reply. + */ +function send_cmd($command) +{ + $sock = open_socket(); + if ($sock == false) { + return ""; + } + socket_write($sock, $command . "\r\n", 4096); + + $answer = ""; + while (1) { + $line = socket_read($sock, 4096); + if ($line === '') + break; + $answer .= $line; + } + socket_close($sock); + + return $answer; +} + + +function startsWith($haystack, $needle) +{ + return !strncmp($haystack, $needle, strlen($needle)); +} + + +$response = array( + 'error' => false, + 'msg' => 'Ok', +); + + +if (isset($_POST['update'])) { + + $cmd = "DEVICE PUT " . $_POST['uuid'] . "\r\n"; + $cmd .= "TYPE," . $_POST['type'] . "\r\n"; + $cmd .= "DIRECTION," . $_POST['direction'] . "\r\n"; + $cmd .= "VALUE," . $_POST['value'] . "\r\n"; // Only for outputs + $cmd .= "OFFSET," . $_POST['offset'] . "\r\n"; // Only analog + $cmd .= "PRESENT," . $_POST['present'] . "\r\n"; + $cmd .= "ADDRESS," . $_POST['address'] . "\r\n"; // Not for auto detected + $cmd .= "SUBDEVICE," . $_POST['subdevice'] . "\r\n"; // Not for auto detected + $cmd .= "GPIOPIN," . $_POST['gpiopin'] . "\r\n"; + $cmd .= "DESCRIPTION," . $_POST['description'] . "\r\n"; + $cmd .= "COMMENT," . $_POST['comment'] . "\r\n"; + $cmd .= "."; + $answer = send_cmd($cmd); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "219")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['add'])) { + + $answer = send_cmd("DEVICE ADD " . $_POST['type']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['del'])) { + + $answer = send_cmd("DEVICE DEL " . $_POST['uuid']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else { + + $answer = send_cmd("DEVICE JSON"); + header("Content-type: application/json"); + + $arr = explode("\r\n", $answer); + if (startsWith($arr[0], "212")) { + echo $arr[1]; + } else { + echo '{}'; + } +} + +?>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/dbsimulators.php Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,126 @@ +<?php + + +function open_socket() +{ + $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); + + if (!($sock === false)) { + if (socket_connect($sock, "localhost", 6554)) { + socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 15, 'usec' => 0)); + } else { + socket_close($sock); + } + } + return $sock; +} + + +/** + * @param string $command to send to the server. + * @return string with the complete reply from the + * server. This can be a multiline reply. + */ +function send_cmd($command) +{ + $sock = open_socket(); + if ($sock == false) { + return ""; + } + socket_write($sock, $command . "\r\n", 4096); + + $answer = ""; + while (1) { + $line = socket_read($sock, 4096); + if ($line === '') + break; + $answer .= $line; + } + socket_close($sock); + + return $answer; +} + + +function startsWith($haystack, $needle) +{ + return !strncmp($haystack, $needle, strlen($needle)); +} + + +$response = array( + 'error' => false, + 'msg' => 'Ok', +); + + +if (isset($_POST['update'])) { + + $cmd = "SIMULATOR PUT " . $_POST['uuid'] . "\r\n"; + $cmd .= "NAME," . $_POST['name'] . "\r\n"; + $cmd .= "VOLUME_AIR," . $_POST['volume_air'] . "\r\n"; + $cmd .= "VOLUME_BEER," . $_POST['volume_beer'] . "\r\n"; + $cmd .= "ROOM_TEMPERATURE," . $_POST['room_temperature'] . "\r\n"; + $cmd .= "ROOM_HUMIDITY," . $_POST['room_humidity'] . "\r\n"; + $cmd .= "AIR_PRESENT," . $_POST['air_present'] . "\r\n"; + $cmd .= "BEER_PRESENT," . $_POST['beer_present'] . "\r\n"; + $cmd .= "BEER_PRESENT2," . $_POST['beer_present2'] . "\r\n"; + $cmd .= "CHILLER_PRESENT," . $_POST['chiller_present'] . "\r\n"; + $cmd .= "COOLER_TEMP," . $_POST['cooler_temp'] . "\r\n"; + $cmd .= "COOLER_TIME," . $_POST['cooler_time'] . "\r\n"; + $cmd .= "COOLER_SIZE," . $_POST['cooler_size'] . "\r\n"; + $cmd .= "COOLER_PRESENT," . $_POST['cooler_present'] . "\r\n"; + $cmd .= "COOLER_POWER," . $_POST['cooler_power'] . "\r\n"; + $cmd .= "HEATER_TEMP," . $_POST['heater_temp'] . "\r\n"; + $cmd .= "HEATER_TIME," . $_POST['heater_time'] . "\r\n"; + $cmd .= "HEATER_SIZE," . $_POST['heater_size'] . "\r\n"; + $cmd .= "HEATER_PRESENT," . $_POST['heater_present'] . "\r\n"; + $cmd .= "HEATER_POWER," . $_POST['heater_power'] . "\r\n"; + $cmd .= "FAN_PRESENT," . $_POST['fan_present'] . "\r\n"; + $cmd .= "FAN_POWER," . $_POST['fan_power'] . "\r\n"; + $cmd .= "LIGHT_PRESENT," . $_POST['light_present'] . "\r\n"; + $cmd .= "LIGHT_POWER," . $_POST['light_power'] . "\r\n"; + $cmd .= "FRIGO_ISOLATION," . $_POST['frigo_isolation'] . "\r\n"; + $cmd .= "."; + $answer = send_cmd($cmd); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "219")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['add'])) { + + $answer = send_cmd("SIMULATOR ADD " . $_POST['name']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else if (isset($_POST['del'])) { + + $answer = send_cmd("SIMULATOR DEL " . $_POST['uuid']); + $arr = explode("\r\n", $answer); + if (! startsWith($arr[0], "211")) { + $response['error'] = true; + $response['msg'] = $arr[0]; + } + exit(json_encode($response)); + +} else { + + $answer = send_cmd("SIMULATOR JSON"); + header("Content-type: application/json"); + + $arr = explode("\r\n", $answer); + if (startsWith($arr[0], "212")) { + echo $arr[1]; + } else { + echo '{}'; + } +} + +?>
--- a/www/getdevices.php Sun Apr 28 15:50:42 2024 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +0,0 @@ -<?php - - -function open_socket() -{ - $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); - - if (!($sock === false)) { - if (socket_connect($sock, "localhost", 6554)) { - socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array('sec' => 15, 'usec' => 0)); - } else { - socket_close($sock); - } - } - return $sock; -} - - -/** - * @param string $command to send to the server. - * @return string with the complete reply from the - * server. This can be a multiline reply. - */ -function send_cmd($command) -{ - $sock = open_socket(); - if ($sock == false) { - return ""; - } - socket_write($sock, $command . "\r\n", 4096); - - $answer = ""; - while (1) { - $line = socket_read($sock, 4096); - if ($line === '') - break; - $answer .= $line; - } - socket_close($sock); - - return $answer; -} - - -function startsWith($haystack, $needle) -{ - return !strncmp($haystack, $needle, strlen($needle)); -} - - -$response = array( - 'error' => false, - 'msg' => 'Ok', -); - - -if (isset($_POST['update'])) { - - $cmd = "DEVICE PUT " . $_POST['uuid'] . "\r\n"; - $cmd .= "TYPE," . $_POST['type'] . "\r\n"; - $cmd .= "DIRECTION," . $_POST['direction'] . "\r\n"; - $cmd .= "VALUE," . $_POST['value'] . "\r\n"; // Only for outputs - $cmd .= "OFFSET," . $_POST['offset'] . "\r\n"; // Only analog - $cmd .= "PRESENT," . $_POST['present'] . "\r\n"; - $cmd .= "ADDRESS," . $_POST['address'] . "\r\n"; // Not for auto detected - $cmd .= "SUBDEVICE," . $_POST['subdevice'] . "\r\n"; // Not for auto detected - $cmd .= "GPIOPIN," . $_POST['gpiopin'] . "\r\n"; - $cmd .= "DESCRIPTION," . $_POST['description'] . "\r\n"; - $cmd .= "COMMENT," . $_POST['comment'] . "\r\n"; - $cmd .= "."; - $answer = send_cmd($cmd); - $arr = explode("\r\n", $answer); - if (! startsWith($arr[0], "219")) { - $response['error'] = true; - $response['msg'] = $arr[0]; - } - exit(json_encode($response)); - -} else if (isset($_POST['add'])) { - - $answer = send_cmd("DEVICE ADD " . $_POST['type']); - $arr = explode("\r\n", $answer); - if (! startsWith($arr[0], "211")) { - $response['error'] = true; - $response['msg'] = $arr[0]; - } - exit(json_encode($response)); - -} else if (isset($_POST['del'])) { - - $answer = send_cmd("DEVICE DEL " . $_POST['uuid']); - $arr = explode("\r\n", $answer); - if (! startsWith($arr[0], "211")) { - $response['error'] = true; - $response['msg'] = $arr[0]; - } - exit(json_encode($response)); - -} else { - - $answer = send_cmd("DEVICE JSON"); - header("Content-type: application/json"); - - $arr = explode("\r\n", $answer); - if (startsWith($arr[0], "212")) { - echo $arr[1]; - } else { - echo '{}'; - } -} - -?>
--- a/www/js/global.js Sun Apr 28 15:50:42 2024 +0200 +++ b/www/js/global.js Tue Apr 30 17:26:41 2024 +0200 @@ -83,10 +83,14 @@ Show0dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 0 }, Show1dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 1 }, +Show2dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 2 }, +Show3dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 3 }, +Show4dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 4 }, Spin0dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 0, spinButtons: true }, Spin1dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 1, spinButtons: true }, Spin2dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 2, spinButtons: true }, Spin3dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 3, spinButtons: true }, +Spin4dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 4, spinButtons: true }, PosInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 0, spinButtons: true }, SubInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, max: 63, decimalDigits: 0, spinButtons: true }, GPIOInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: -1, max:31, decimalDigits: 0, spinButtons: true },
--- a/www/js/set_devices.js Sun Apr 28 15:50:42 2024 +0200 +++ b/www/js/set_devices.js Tue Apr 30 17:26:41 2024 +0200 @@ -78,7 +78,7 @@ $(document).ready(function() { var dataRecord = {}, - url = 'getdevices.php', + url = 'dbdevices.php', source = { datatype: 'json', cache: false,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/js/set_simulators.js Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,358 @@ +/***************************************************************************** + * Copyright (C) 2024 + * + * Michiel Broek <mbroek at mbse dot eu> + * + * This file is part of mbsePi-apps + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * BrewCloud 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 General Public License + * along with ThermFerm; see the file COPYING. If not, write to the Free + * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + *****************************************************************************/ + + +function createDelElements() { + $('#eventWindow').jqxWindow({ + theme: theme, + position: { x: 430, y: 210 }, + width: 420, + height: 175, + resizable: false, + isModal: true, + modalOpacity: 0.4, + okButton: $('#delOk'), + cancelButton: $('#delCancel'), + initContent: function() { + $('#delOk').jqxButton({ template: 'danger', width: '65px', theme: theme }); + $('#delCancel').jqxButton({ template: 'success', width: '65px', theme: theme }); + $('#delCancel').focus(); + } + }); + $('#eventWindow').jqxWindow('hide'); +} + + +function createAddElements() { + $('#addWindow').jqxWindow({ + theme: theme, + position: { x: 400, y: 210 }, + width: 480, + height: 180, + resizable: false, + isModal: true, + modalOpacity: 0.4, + okButton: $('#addOk'), + cancelButton: $('#addCancel'), + initContent: function() { + $('#addOk').jqxButton({ template: 'success', width: '65px', theme: theme }); + $('#addCancel').jqxButton({ template: 'primary', width: '65px', theme: theme }); + $('#addCancel').focus(); + } + }); + $('#addWindow').jqxWindow('hide'); +} + + + +$(document).ready(function() { + var dataRecord = {}, + url = 'dbsimulators.php', + source = { + datatype: 'json', + cache: false, + datafields: [ + { name: 'uuid', type: 'string' }, + { name: 'name', type: 'string' }, + { name: 'simno', type: 'int' }, + { name: 'volume_air', type: 'int' }, + { name: 'volume_beer', type: 'int' }, + { name: 'room_tempaddress', map: 'room>tempaddress' }, + { name: 'room_temperature', map: 'room>temperature', type: 'float' }, + { name: 'room_humaddress', map: 'room>humaddress' }, + { name: 'room_humidity', map: 'room>humidity', type: 'float' }, + { name: 'air_address', map: 'air>address' }, + { name: 'air_temperature', map: 'air>temperature', type: 'float' }, + { name: 'air_present', map: 'air>present' }, + { name: 'beer_address', map: 'beer>address' }, + { name: 'beer_temperature', map: 'beer>temperature', type: 'float' }, + { name: 'beer_present', map: 'beer>present' }, + { name: 'beer_address2', map: 'beer2>address' }, + { name: 'beer_temperature2', map: 'beer2>temperature', type: 'float' }, + { name: 'beer_present2', map: 'beer2>present' }, + { name: 'chiller_address', map: 'chiller>address' }, + { name: 'chiller_temperature', map: 'chiller>temperature', type: 'float' }, + { name: 'chiller_present', map: 'chiller>present' }, + { name: 'cooler_address', map: 'cooler>address' }, + { name: 'cooler_temp', map: 'cooler>temperature', type: 'float' }, + { name: 'cooler_time', map: 'cooler>time', type: 'int' }, + { name: 'cooler_size', map: 'cooler>size', type: 'float' }, + { name: 'cooler_present', map: 'cooler>present' }, + { name: 'cooler_power', map: 'cooler>power', type: 'int' }, + { name: 'heater_address', map: 'heater>address' }, + { name: 'heater_temp', map: 'heater>temperature', type: 'float' }, + { name: 'heater_time', map: 'heater>time', type: 'int' }, + { name: 'heater_size', map: 'heater>size', type: 'float' }, + { name: 'heater_present', map: 'heater>present' }, + { name: 'heater_power', map: 'heater>power', type: 'int' }, + { name: 'fan_address', map: 'fan>address' }, + { name: 'fan_present', map: 'fan>present' }, + { name: 'fan_power', map: 'fan>power', type: 'int' }, + { name: 'light_address', map: 'light>address' }, + { name: 'light_present', map: 'light>present' }, + { name: 'light_power', map: 'light>power', type: 'int' }, + { name: 'frigo_isolation', type: 'float' }, + { name: 'timestamp', type: 'int' } + ], + id: 'uuid', + url: url + }, + dataAdapter = new $.jqx.dataAdapter(source), + editrow = -1, + tzoffset = (new Date()).getTimezoneOffset() * 60000; //offset in milliseconds + + // initialize the input fields. + $('#name').jqxInput({ theme: theme, width: 180, height: 23 }); + $('#simno').jqxNumberInput(Show0dec); + $('#uuid').jqxInput({ theme: theme, width: 360, height: 23 }); + $('#room_temperature,#room_humidity').jqxNumberInput(Perc1dec); + $('#volume_air,#volume_beer').jqxNumberInput(PosInt); + $('#frigo_isolation').jqxNumberInput(Spin3dec); + $('#timestamp').jqxInput({ theme: theme, width: 180, height: 23 }); + + $('#air_address,#beer_address,#beer_address2,#chiller_address,#cooler_address,#heater_address,#fan_address,#light_address').jqxInput({ theme: theme, width: 180, height: 23 }); + $('#air_temperature,#beer_temperature,#beer_temperature2,#chiller_temperature').jqxNumberInput(Show4dec); + $('#air_present,#beer_present,#beer_present2,#chiller_present,#cooler_present,#heater_present,#fan_present,#light_present').jqxDropDownList({ + theme: theme, + source: DevicePresentAdapter, + valueMember: 'mno', + displayMember: 'en', + width: 180, + height: 23, + autoDropDownHeight: true + }); + $('#cooler_power,#heater_power,#fan_power,#light_power').jqxNumberInput(Perc0); + $('#cooler_temp,#heater_temp').jqxNumberInput(Spin1dec); + $('#cooler_time,#heater_time').jqxNumberInput(PosInt); + $('#cooler_size,#heater_size').jqxNumberInput(Spin4dec); + + // initialize jqxGrid + $('#jqxgrid').jqxGrid({ + width: 1280, + height: 630, + source: dataAdapter, + theme: theme, + showstatusbar: true, + renderstatusbar: function(statusbar) { + var rowCount = $("#jqxgrid").jqxGrid('getrows').length; + statusbar.append('<div style="float: left; margin: 8px; color: orange !important;">Total items: ' + rowCount + '</div>'); + var container, addButton; + container = $('<div style="overflow: hidden; position: relative; margin: 5px;"></div>'); + addButton = $('<div style="float: right; margin-right: 15px;"><img style="position: relative; margin-top: 2px;" ' + + 'src="images/add.png"/><span style="margin-left: 4px; position: relative; top: -4px;">New</span></div>'); + container.append(addButton); + statusbar.append(container); + addButton.jqxButton({ theme: theme, width: 90, height: 17 }); + // add new row. + addButton.click(function(event) { + $('#addWindow').jqxWindow('open'); + $('#addName').jqxInput({ theme: theme, width: 180, height: 23 }); + $('#addOk').click(function() { + var data, + data = 'add=true&name=' + $('#addName').val(); + console.log(data); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: 'POST', + success: function(data) { + if (data.error) { + console.log('add: ' + data.msg); + alert('Error: ' + data.msg); + } else { + console.log('add: success'); + } + }, + error: function(jqXHR, textStatus, errorThrown) {} + }); + $('#jqxgrid').jqxGrid('updatebounddata'); + }); + }); + }, + columns: [ + { text: 'Name', datafield: 'name' }, + { text: 'No.', datafield: 'simno', width: 80 }, + { text: 'Air temp', datafield: 'air_temperature', width: 100 }, + { text: 'Beer temp', datafield: 'beer_temperature', width: 100 }, + { text: 'Heater', datafield: 'heater_power', width: 80 }, + { text: 'Cooler', datafield: 'cooler_power', width: 80 }, + { text: 'Fan', datafield: 'fan_power', width: 80 }, + { text: 'Last change', datafield: 'timestamp', width: 190, + cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { + var date = new Date((value * 1000) - tzoffset).toISOString().slice(0, 19).replace("T", " "); + return '<span style="margin: 3px; margin-top: 6px; float: left;">' + date + '</span>'; + } + }, + { text: '', datafield: 'Edit', width: 100, align: 'center', columntype: 'button', cellsrenderer: function() { + return 'Edit'; + }, buttonclick: function(row) { + // open the popup window when the user clicks a button. + editrow = row; + $('#popupWindow').jqxWindow({ position: { x: 40, y: 15 } }); + dataRecord = $('#jqxgrid').jqxGrid('getrowdata', editrow); + $('#name').val(dataRecord.name); + $('#uuid').val(dataRecord.uuid); + $('#simno').val(dataRecord.simno); + $('#room_temperature').val(dataRecord.room_temperature); + $('#room_humidity').val(dataRecord.room_humidity); + $('#volume_air').val(dataRecord.volume_air); + $('#volume_beer').val(dataRecord.volume_beer); + $('#frigo_isolation').val(dataRecord.frigo_isolation); + + $('#air_address').val(dataRecord.air_address); + $('#air_temperature').val(dataRecord.air_temperature); + $('#air_present').val(dataRecord.air_present); + $('#beer_address').val(dataRecord.beer_address); + $('#beer_temperature').val(dataRecord.beer_temperature); + $('#beer_present').val(dataRecord.beer_present); + $('#beer_address2').val(dataRecord.beer_address2); + $('#beer_temperature2').val(dataRecord.beer_temperature2); + $('#beer_present2').val(dataRecord.beer_present2); + $('#chiller_address').val(dataRecord.chiller_address); + $('#chiller_temperature').val(dataRecord.chiller_temperature); + $('#chiller_present').val(dataRecord.chiller_present); + $('#cooler_address').val(dataRecord.cooler_address); + $('#cooler_power').val(dataRecord.cooler_power); + $('#cooler_present').val(dataRecord.cooler_present); + $('#cooler_temp').val(dataRecord.cooler_temp); + $('#cooler_time').val(dataRecord.cooler_time); + $('#cooler_size').val(dataRecord.cooler_size); + $('#heater_address').val(dataRecord.heater_address); + $('#heater_power').val(dataRecord.heater_power); + $('#heater_present').val(dataRecord.heater_present); + $('#heater_temp').val(dataRecord.heater_temp); + $('#heater_time').val(dataRecord.heater_time); + $('#heater_size').val(dataRecord.heater_size); + $('#fan_address').val(dataRecord.fan_address); + $('#fan_power').val(dataRecord.fan_power); + $('#fan_present').val(dataRecord.fan_present); + $('#light_address').val(dataRecord.light_address); + $('#light_power').val(dataRecord.light_power); + $('#light_present').val(dataRecord.light_present); + var date = new Date((dataRecord.timestamp * 1000) - tzoffset).toISOString().slice(0, 19).replace("T", " "); + $('#timestamp').val(date); + // show the popup window. + $('#popupWindow').jqxWindow('open'); + } + } + ], + }); + + // initialize the popup window and buttons. + $('#popupWindow').jqxWindow({ + width: 1280, + height: 625, + resizable: false, + theme: theme, + isModal: true, + autoOpen: false, + cancelButton: $('#Cancel'), + modalOpacity: 0.40 + }); + $('#popupWindow').on('open', function() { + $('#dev_description').jqxInput('selectAll'); + }); + $('#Delete').jqxButton({ template: 'danger', width: '90px', theme: theme }); + $('#Delete').click(function() { + if (editrow >= 0) { + // Open a popup to confirm this action. + $('#eventWindow').jqxWindow('open'); + $('#delOk').click(function() { + var data, + data = 'del=true&uuid=' + $('#dev_uuid').val(); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: 'POST', + success: function(data) { + if (data.error) { + console.log('del: ' + data.msg); + alert('Error: ' + data.msg); + } else { + console.log('del: success'); + } + }, + error: function(jqXHR, textStatus, errorThrown) {} + }); + $('#jqxgrid').jqxGrid('updatebounddata'); + }); + } + $('#popupWindow').jqxWindow('hide'); + }); + $('#Cancel').jqxButton({ template: 'primary', width: '90px', theme: theme }); + $('#Save').jqxButton({ template: 'success', width: '90px', theme: theme }); + $('#Save').click(function() { + var data, + row = { + uuid: dataRecord.uuid, + type: $('#dev_type').val(), + direction: $('#dev_direction').val(), + value: parseInt($('#dev_value').jqxNumberInput('decimal')), + offset: parseInt($('#dev_offset').jqxNumberInput('decimal')), + present: $('#dev_present').val(), + address: $('#dev_address').val(), + subdevice: parseInt($('#dev_subdevice').jqxNumberInput('decimal')), + gpiopin: parseInt($('#dev_gpiopin').jqxNumberInput('val')), + description: $('#dev_description').val(), + comment: $('#dev_comment').val() + }; + data = 'update=true&' + $.param(row); + console.log(data); + $.ajax({ + dataType: 'json', + url: url, + cache: false, + data: data, + type: 'POST', + success: function(data) { + if (data.error) { + console.log('update: ' + data.msg); + alert('Error: ' + data.msg); + } else { + console.log('update: success'); + } + }, + error: function(jqXHR, textStatus, errorThrown) {} + }); + $('#popupWindow').jqxWindow('hide'); + }); + createDelElements(); + createAddElements(); + + websocket.onmessage = function(evt) { + var msg = evt.data; + var obj = JSON.parse(msg); + + if (obj.ping) { + websocket.send('{"pong":' + obj.ping + '}'); + } + + if (obj.type == 'simulator') { + // Use the message to trigger update. + $('#jqxgrid').jqxGrid('updatebounddata'); + } + } +});
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/www/set_simulators.php Tue Apr 30 17:26:41 2024 +0200 @@ -0,0 +1,175 @@ +<?php +require_once($_SERVER['DOCUMENT_ROOT'].'/includes/global.inc.php'); +page_header('Simulator engines', 'set_simulators'); +?> + + <div id="jqxgrid"></div> + <div style="margin-top: 30px;"> + <div id="cellbegineditevent"></div> + <div style="margin-top: 10px;" id="cellendeditevent"></div> + </div> + + <!-- Popup editor window. --> + <div id="popupWindow"> + <div>Edit simulator</div> + <div style="overflow: hidden;"> + <table style="width: 100%;"> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Name:</td> + <td colspan="3" align="left" style="vertical-align: top; padding: 3px;"><input id="name" /></td> + <td style="vertical-align: top; float: right; padding: 3px;">No:</td> + <td style="padding: 3px;"><div style="float: left;" id="simno"></div></td> + <td></td><td></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Temperature:</td> + <td style="padding: 3px;"><div style="float: left;" id="room_temperature"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Humidity:</td> + <td style="padding: 3px;"><div style="float: left;" id="room_humidity"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Volume air:</td> + <td style="padding: 3px;"><div style="float: left;" id="volume_air"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Volume beer:</td> + <td style="padding: 3px;"><div style="float: left;" id="volume_beer"></div></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Frigo isulation:</td> + <td style="padding: 3px;"><div style="float: left;" id="frigo_isolation"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Last update:</td> + <td style="vertical-align: top; padding: 3px;"><input id="timestamp" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Uuid:</td> + <td align="left" colspan="3" style="vertical-align: top; padding: 3px;"><input id="uuid" readonly /></td> + </tr> + <tr><td colspan="8"><hr></td></tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Air address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="air_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Temperature:</td> + <td style="padding: 3px;"><div style="float: left;" id="air_temperature"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="air_present"></div></td> + <td></td><td></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Beer address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="beer_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Temperature:</td> + <td style="padding: 3px;"><div style="float: left;" id="beer_temperature"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="beer_present"></div></td> + <td></td><td></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Beer alternate:</td> + <td style="vertical-align: top; padding: 3px;"><input id="beer_address2" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Temperature:</td> + <td style="padding: 3px;"><div style="float: left;" id="beer_temperature2"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="beer_present2"></div></td> + <td></td><td></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Chiller address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="chiller_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Temperature:</td> + <td style="padding: 3px;"><div style="float: left;" id="chiller_temperature"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="chiller_present"></div></td> + <td></td><td></td> + </tr> + + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Cooler address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="cooler_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Power:</td> + <td style="padding: 3px;"><div style="float: left;" id="cooler_power"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="cooler_present"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Min temp:</td> + <td style="padding: 3px;"><div style="float: left;" id="cooler_temp"></div></td> + </tr> + <tr> + <td></td><td></td> + <td></td><td></td> + <td style="vertical-align: top; float: right; padding: 3px;">Time:</td> + <td style="padding: 3px;"><div style="float: left;" id="cooler_time"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Size:</td> + <td style="padding: 3px;"><div style="float: left;" id="cooler_size"></div></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Heater address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="heater_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Power:</td> + <td style="padding: 3px;"><div style="float: left;" id="heater_power"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="heater_present"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Max temp:</td> + <td style="padding: 3px;"><div style="float: left;" id="heater_temp"></div></td> + </tr> + <tr> + <td></td><td></td> + <td></td><td></td> + <td style="vertical-align: top; float: right; padding: 3px;">Time:</td> + <td style="padding: 3px;"><div style="float: left;" id="heater_time"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Size:</td> + <td style="padding: 3px;"><div style="float: left;" id="heater_size"></div></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Fan address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="fan_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Power:</td> + <td style="padding: 3px;"><div style="float: left;" id="fan_power"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="fan_present"></div></td> + <td></td><td></td> + </tr> + <tr> + <td style="vertical-align: top; float: right; padding: 3px;">Light address:</td> + <td style="vertical-align: top; padding: 3px;"><input id="light_address" readonly /></td> + <td style="vertical-align: top; float: right; padding: 3px;">Power:</td> + <td style="padding: 3px;"><div style="float: left;" id="light_power"></div></td> + <td style="vertical-align: top; float: right; padding: 3px;">Present:</td> + <td style="padding: 3px;"><div style="float: left;" id="light_present"></div></td> + <td></td><td></td> + </tr> + + + + <tr><td colspan="8"></td></tr> + <tr> + <td style="padding-top: 20px;" align="right"><input type="button" id="Delete" value="Delete" /></td> + <td></td> + <td></td><td></td><td></td> + <td style="padding-top: 20px;" align="right"><input style="margin-right: 5px;" type="button" id="Save" value="Save" /></td> + <td></td> + <td style="padding-top: 20px;" align="left"><input id="Cancel" type="button" value="Cancel" /></td> + </tr> + </table> + </div> + </div> + + + <div id="addWindow"> + <div> + Add new device + </div> + <div> + <div> + Press "OK" to create a new simulator record.<br> + Press "Cancel" to close without adding a new simulator. + </div> + <div style="margin-top: 10px;"> + <div style="float: right; vertical-align: top; padding: 10px; margin-right: 120px">Name:<input id="addName" /></div> + </div> + <div> + <div style="float: right; margin-top: 10px; margin-bottom: 10px;"> + <input type="button" id="addOk" value="OK" style="margin-right: 15px" /> + <input type="button" id="addCancel" value="Cancel" style="margin-right: 160px" /> + </div> + </div> + </div> + </div> + +<?php +confirm_delete(); +page_footer(); +?>