This commit is contained in:
Striker72rus 2026-03-20 14:31:39 +03:00
parent 87670de0a7
commit d0062c0a64
11 changed files with 3389 additions and 93 deletions

View File

@ -68,3 +68,34 @@ POST /api.php?action=save-entity-override
}
```
## Empty room slots
Для desktop-раскладки можно создавать пустые слоты:
```bash
POST /api.php?action=create-room-layout-item
{
"room_id": "living_room"
}
```
Перемещение:
```bash
POST /api.php?action=save-room-layout-item
{
"room_id": "living_room",
"layout_item_id": "slot_xxx",
"order": 120
}
```
Удаление:
```bash
POST /api.php?action=delete-room-layout-item
{
"room_id": "living_room",
"layout_item_id": "slot_xxx"
}
```

72
api.php
View File

@ -127,10 +127,82 @@ try {
'icon' => array_key_exists('icon', $payload) ? (string)$payload['icon'] : null,
];
if (array_key_exists('temperature_sensor_entity_id', $payload)) {
$patch['temperature_sensor_entity_id'] = (string)$payload['temperature_sensor_entity_id'];
}
$config = app_update_room_override($config, $roomId, $patch);
api_json(['ok' => true, 'config' => ['rooms' => $config['rooms']]]);
}
if ($action === 'create-room-layout-item') {
$payload = api_input();
$roomId = trim((string)($payload['room_id'] ?? ''));
if ($roomId === '') {
api_json(['ok' => false, 'error' => 'room_id is required'], 400);
}
$layoutItemId = trim((string)($payload['layout_item_id'] ?? ''));
if ($layoutItemId === '') {
$layoutItemId = 'slot_' . str_replace('.', '', uniqid('', true));
}
$order = array_key_exists('order', $payload) ? (int)$payload['order'] : null;
$config = app_update_room_layout_item($config, $roomId, $layoutItemId, [
'order' => $order,
]);
api_json([
'ok' => true,
'layout_item_id' => $layoutItemId,
'config' => ['rooms' => $config['rooms']],
]);
}
if ($action === 'save-room-layout-item') {
$payload = api_input();
$roomId = trim((string)($payload['room_id'] ?? ''));
$layoutItemId = trim((string)($payload['layout_item_id'] ?? ''));
if ($roomId === '' || $layoutItemId === '') {
api_json(['ok' => false, 'error' => 'room_id and layout_item_id are required'], 400);
}
$patch = [
'order' => array_key_exists('order', $payload) ? (int)$payload['order'] : null,
];
$config = app_update_room_layout_item($config, $roomId, $layoutItemId, $patch);
api_json(['ok' => true, 'config' => ['rooms' => $config['rooms']]]);
}
if ($action === 'delete-room-layout-item') {
$payload = api_input();
$roomId = trim((string)($payload['room_id'] ?? ''));
$layoutItemId = trim((string)($payload['layout_item_id'] ?? ''));
if ($roomId === '' || $layoutItemId === '') {
api_json(['ok' => false, 'error' => 'room_id and layout_item_id are required'], 400);
}
$config = app_delete_room_layout_item($config, $roomId, $layoutItemId);
api_json(['ok' => true, 'config' => ['rooms' => $config['rooms']]]);
}
if ($action === 'reorder-room-grid') {
$payload = api_input();
$roomId = trim((string)($payload['room_id'] ?? ''));
$entries = $payload['entries'] ?? [];
if ($roomId === '' || !is_array($entries)) {
api_json(['ok' => false, 'error' => 'room_id and entries are required'], 400);
}
$config = app_reorder_room_grid($config, $roomId, $entries);
api_json(['ok' => true, 'config' => ['rooms' => $config['rooms']]]);
}
if ($action === 'save-settings') {
$payload = api_input();
if (array_key_exists('edit_mode', $payload)) {

View File

@ -144,7 +144,7 @@ textarea {
}
.content-header__back {
display: none;
display: none !important;
flex: 0 0 auto;
}
@ -157,6 +157,20 @@ textarea {
align-items: center;
gap: 10px;
margin-left: 16px;
flex-wrap: wrap;
justify-content: flex-end;
margin-left: auto;
}
.content-header__ghost-button {
min-height: 40px;
padding-inline: 12px;
border-radius: 12px;
}
.content-header__ghost-button span {
font-size: 13px;
font-weight: 700;
}
.icon-button {
@ -252,6 +266,25 @@ textarea {
min-height: 132px;
}
.room-item.is-battery-room {
background:
radial-gradient(circle at top right, rgba(255, 193, 7, 0.12), transparent 40%),
linear-gradient(180deg, rgba(32, 28, 20, 0.94), rgba(21, 20, 18, 0.95));
border-color: rgba(255, 193, 7, 0.18);
}
.room-item.is-battery-room .room-item__icon {
color: #ffc94d;
--icon-node-img-filter: brightness(0) saturate(100%) invert(79%) sepia(41%) saturate(909%) hue-rotate(357deg) brightness(103%) contrast(101%);
}
.room-item.is-battery-room.is-selected {
background:
radial-gradient(circle at top right, rgba(255, 193, 7, 0.16), transparent 40%),
linear-gradient(180deg, rgba(51, 43, 20, 0.96), rgba(24, 22, 18, 0.98));
border-color: rgba(255, 193, 7, 0.38);
}
.room-item__icon {
width: 50px;
height: 50px;
@ -323,27 +356,6 @@ textarea {
color: var(--text-muted);
}
.room-item__status {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 5px;
flex-wrap: wrap;
margin-top: auto;
}
.room-item__count {
min-width: 22px;
padding: 3px 6px;
border-radius: 999px;
background: rgba(255,255,255,0.04);
color: var(--text-subtle);
font-size: 10px;
font-weight: 700;
text-align: center;
}
.room-item__temp {
position: absolute;
top: 10px;
@ -504,6 +516,59 @@ textarea {
filter: saturate(0.8);
}
.grid-card--ghost {
background: transparent;
border-color: transparent;
box-shadow: none;
min-height: 120px;
}
.grid-card--ghost .grid-card__inner {
justify-content: space-between;
gap: 10px;
}
.grid-card--ghost .grid-card__header {
gap: 8px;
}
.grid-card--ghost .grid-card__icon {
background: rgba(255,255,255,0.02);
border-color: rgba(255,255,255,0.05);
color: rgba(237, 240, 246, 0.42);
}
.grid-card--ghost .grid-card__title {
font-size: 18px;
}
.grid-card--ghost .grid-card__subtitle {
margin-top: 2px;
font-size: 13px;
}
.grid-card--ghost .grid-card__footer--edit {
margin-top: auto;
}
.grid-card--ghost.is-editing {
background:
radial-gradient(circle at top left, rgba(103, 214, 255, 0.08), transparent 35%),
rgba(255, 255, 255, 0.01);
border-color: rgba(103, 214, 255, 0.18);
border-style: dashed;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.04);
}
.grid-card--ghost.is-editing .grid-card__title-line,
.grid-card--ghost.is-editing .grid-card__subtitle {
color: var(--text-muted);
}
.grid-card--ghost.is-editing .grid-card__icon {
color: var(--accent);
}
.main-dashboard__cards .grid-card--door {
transition: background 220ms ease, border-color 220ms ease, box-shadow 220ms ease, transform 220ms ease, filter 220ms ease;
}
@ -775,6 +840,16 @@ textarea {
min-height: 42px;
}
.grid-card__layout-settings {
display: grid;
gap: 10px;
margin-top: 2px;
}
.grid-card__layout-settings .mushroom-button {
width: 100%;
}
.mushroom-button {
min-height: 48px;
padding: 10px 12px;
@ -1191,6 +1266,129 @@ body.is-mobile-ui #camera-modal {
flex-direction: column;
}
.temperature-sensor-modal {
width: min(62vw, 760px);
max-height: 84vh;
border-radius: 30px;
border: 1px solid rgba(255,255,255,0.08);
background: linear-gradient(180deg, rgba(20, 22, 29, 0.98), rgba(12, 14, 18, 0.98));
box-shadow: 0 24px 72px rgba(0, 0, 0, 0.5);
overflow: hidden;
display: flex;
flex-direction: column;
}
.temperature-sensor-modal__header {
padding: 18px 20px;
border-bottom: 1px solid rgba(255,255,255,0.06);
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
}
.temperature-sensor-modal__eyebrow {
color: var(--text-subtle);
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 12px;
min-height: 14px;
}
.temperature-sensor-modal__title {
margin-top: 5px;
font-size: 18px;
font-weight: 700;
}
.temperature-sensor-modal__body {
padding: 18px;
display: grid;
gap: 14px;
overflow: auto;
}
.temperature-sensor-modal__current {
display: grid;
gap: 8px;
padding: 14px 16px;
border-radius: 18px;
border: 1px solid rgba(255,255,255,0.08);
background: rgba(255,255,255,0.03);
}
.temperature-sensor-modal__current-label {
color: var(--text-subtle);
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 11px;
}
.temperature-sensor-modal__current-value {
font-size: 16px;
font-weight: 700;
}
.temperature-sensor-modal__empty {
color: var(--text-muted);
padding: 20px 0;
}
.temperature-sensor-modal__list {
display: grid;
gap: 10px;
}
.temperature-sensor-modal__option {
width: 100%;
border: 1px solid rgba(255,255,255,0.08);
background: rgba(255,255,255,0.04);
color: var(--text);
border-radius: 18px;
padding: 12px 14px;
display: grid;
grid-template-columns: 1fr auto;
gap: 10px;
align-items: center;
cursor: pointer;
text-align: left;
transition: transform 180ms ease, background 180ms ease, border-color 180ms ease;
}
.temperature-sensor-modal__option:hover {
transform: translateY(-1px);
background: rgba(255,255,255,0.06);
border-color: var(--border-strong);
}
.temperature-sensor-modal__option.is-active {
background: rgba(103, 214, 255, 0.14);
border-color: rgba(103, 214, 255, 0.28);
}
.temperature-sensor-modal__option-main {
display: grid;
gap: 4px;
min-width: 0;
}
.temperature-sensor-modal__option-name {
font-weight: 700;
line-height: 1.15;
}
.temperature-sensor-modal__option-meta {
color: var(--text-muted);
font-size: 13px;
line-height: 1.2;
}
.temperature-sensor-modal__option-value {
color: var(--accent);
font-weight: 800;
white-space: nowrap;
}
.entity-modal__header {
padding: 18px 20px;
border-bottom: 1px solid rgba(255,255,255,0.06);
@ -1850,6 +2048,171 @@ body.is-mobile-ui #camera-modal {
color: var(--warning);
}
.battery-room {
display: grid;
gap: 14px;
}
.battery-room__header {
align-items: flex-start;
}
.battery-room__list {
display: grid;
gap: 12px;
}
.battery-room__empty {
grid-column: 1 / -1;
}
.battery-card {
min-height: 96px;
border-radius: 24px;
background:
radial-gradient(circle at top right, rgba(255,255,255,0.04), transparent 34%),
linear-gradient(180deg, rgba(27, 30, 37, 0.98), rgba(18, 20, 26, 0.98));
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.battery-card--critical,
.battery-card--empty {
border-color: rgba(255, 125, 125, 0.3);
}
.battery-card--low {
border-color: rgba(255, 191, 84, 0.28);
}
.battery-card--unavailable {
border-color: rgba(237, 240, 246, 0.14);
}
.battery-card--unknown {
border-color: rgba(103, 214, 255, 0.18);
}
.battery-card--ok {
border-color: rgba(136, 240, 199, 0.14);
}
.battery-card__inner {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 14px;
align-items: center;
padding: 16px 18px;
}
.battery-card__main {
display: flex;
align-items: center;
gap: 14px;
min-width: 0;
}
.battery-card__icon {
width: 46px;
height: 46px;
border-radius: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.05);
color: #ffc94d;
flex: 0 0 auto;
}
.battery-card--critical .battery-card__icon,
.battery-card--empty .battery-card__icon {
color: #ff7d7d;
}
.battery-card--low .battery-card__icon {
color: #ffbf54;
}
.battery-card--unavailable .battery-card__icon {
color: rgba(237, 240, 246, 0.52);
}
.battery-card--unknown .battery-card__icon {
color: var(--accent);
}
.battery-card--ok .battery-card__icon {
color: var(--accent-2);
}
.battery-card__text {
min-width: 0;
}
.battery-card__title {
font-size: 18px;
font-weight: 800;
line-height: 1.1;
overflow-wrap: anywhere;
}
.battery-card__source {
margin-top: 5px;
color: var(--text-muted);
font-size: 13px;
}
.battery-card__side {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 3px;
justify-content: center;
text-align: right;
}
.battery-card__percent {
font-size: 26px;
line-height: 1;
font-weight: 800;
color: var(--text);
}
.battery-card--critical .battery-card__percent,
.battery-card--empty .battery-card__percent {
color: #ff7d7d;
}
.battery-card--low .battery-card__percent {
color: #ffbf54;
}
.battery-card--unavailable .battery-card__percent,
.battery-card--unknown .battery-card__percent {
color: var(--text-muted);
}
.battery-card--ok .battery-card__percent {
color: var(--accent-2);
}
.battery-card__status {
color: var(--text-subtle);
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.12em;
}
.battery-card__footer {
grid-column: 1 / -1;
margin-top: -2px;
padding-left: 60px;
color: var(--text-muted);
font-size: 13px;
}
.room-entities-section__grid {
grid-template-columns: repeat(5, minmax(0, 1fr));
width: 100%;
@ -1860,7 +2223,8 @@ body.is-mobile-ui #camera-modal {
.room-entities-section__grid .grid-card--entity-wide,
.room-entities-section__grid .grid-card--cover,
.room-entities-section__grid .grid-card--climate,
.room-entities-section__grid .grid-card--auto {
.room-entities-section__grid .grid-card--auto,
.room-entities-section__grid .grid-card--ghost {
grid-column: span 1;
}
@ -1962,7 +2326,7 @@ body.is-mobile-ui #camera-modal {
}
.app-shell.is-mobile .content-header__back {
display: inline-flex;
display: inline-flex !important;
}
.app-shell.is-mobile .content-header__title {
@ -1986,11 +2350,6 @@ body.is-mobile-ui #camera-modal {
--icon-node-img-filter: brightness(0) saturate(100%) invert(72%) sepia(45%) saturate(1190%) hue-rotate(165deg) brightness(102%) contrast(101%);
}
.app-shell.is-mobile .room-item.is-selected .room-item__count {
background: rgba(255,255,255,0.04);
color: var(--text-subtle);
}
.app-shell.is-mobile .content-top {
margin-top: -12px;
}
@ -2034,10 +2393,15 @@ body.is-mobile-ui #camera-modal {
.room-entities-section__grid .grid-card--entity-wide,
.room-entities-section__grid .grid-card--cover,
.room-entities-section__grid .grid-card--climate,
.room-entities-section__grid .grid-card--auto {
.room-entities-section__grid .grid-card--auto,
.room-entities-section__grid .grid-card--ghost {
grid-column: span 1;
}
.grid-card--ghost {
display: none;
}
.main-quick-action {
min-height: 72px;
}
@ -2063,6 +2427,12 @@ body.is-mobile-ui #camera-modal {
border-radius: 24px;
}
.temperature-sensor-modal {
width: calc(100vw - 20px);
max-height: calc(100dvh - 20px);
border-radius: 24px;
}
.entity-modal__body {
padding: 16px;
gap: 14px;

File diff suppressed because it is too large Load Diff

View File

@ -83,9 +83,7 @@
"poster_url": "http://10.0.6.110:1984/api/frame.jpeg?src=doorbell_main",
"popup_timeout_minutes": 3,
"trigger_entities": [
"binary_sensor.doorbell_person_occupancy",
"binary_sensor.barn_all_occupancy",
"binary_sensor.doorbell_all_occupancy"
"binary_sensor.doorbell_person_occupancy"
]
},
"rooms": [
@ -100,7 +98,7 @@
"visible": false
},
"switch.0xc02cedfffef131dc": {
"order": 9999
"order": 10060
},
"sensor.garage_light_illuminance": {
"order": 10000,
@ -165,9 +163,36 @@
},
"sensor.0x70ac08fffeadbe42_moving": {
"visible": false
},
"cover.garazh_vorota": {
"order": 10000
},
"switch.garage_switch": {
"order": 10010
},
"switch.garazh_zariadka": {
"order": 10050
}
},
"order": 8
"order": 8,
"layout_items": [
{
"id": "slot_69bc55ef2be6c903573086",
"type": "ghost",
"order": 10020
},
{
"id": "slot_69bc55f02ec5d464636840",
"type": "ghost",
"order": 10030
},
{
"id": "slot_69bc55fd89791387446255",
"type": "ghost",
"order": 10040
}
],
"temperature_sensor_entity_id": "sensor.garazh_zariadka_device_temperature"
},
{
"id": "sarai",
@ -185,7 +210,8 @@
"entity_ids": [],
"entity_overrides": {
"remote.detskaia": {
"visible": true
"visible": true,
"order": 10010
},
"update.0x54ef441000d3a2ae": {
"visible": false
@ -204,8 +230,12 @@
},
"media_player.detskaia": {
"visible": false
},
"light.detskaia_svet": {
"order": 10000
}
}
},
"layout_items": []
},
{
"id": "kamery",
@ -315,8 +345,61 @@
},
"update.0x8c8b48fffed1ccf1": {
"visible": false
},
"switch.0x8c8b48fffed1ccf1": {
"order": 10000
},
"switch.terrasa_switch_right": {
"order": 10010
},
"light.terrasa_girlianda_i_svet_l2": {
"order": 10020
},
"light.0x3425b4fffe367dd7": {
"order": 10030
},
"light.terrasa_girlianda_i_svet_l1": {
"order": 10060
},
"cover.0xa4c1388b95d8c6fd": {
"order": 10130
},
"switch.terrasa_switch_left": {
"order": 10050
},
"cover.0xa4c1382b4b27fe39": {
"order": 10100
},
"cover.0xa4c13896938267e3": {
"order": 10110
},
"cover.0xa4c138667b24fd65": {
"order": 10120
}
},
"layout_items": [
{
"id": "slot_69bc51060b6fe832199450",
"type": "ghost",
"order": 10040
},
{
"id": "slot_69bc55bfb6770947221860",
"type": "ghost",
"order": 10070
},
{
"id": "slot_69bc55cab20df172721660",
"type": "ghost",
"order": 10080
},
{
"id": "slot_69bc55cbc357c393673482",
"type": "ghost",
"order": 10090
}
],
"temperature_sensor_entity_id": "sensor.terrasa_temperatura_temperature"
},
{
"id": "ulitsa",
@ -618,8 +701,58 @@
},
"switch.cambarn_push_notifications": {
"visible": false
},
"switch.0x08b95ffffeb5171c": {
"order": 10028
},
"light.sarai_svet": {
"order": 10068
},
"switch.0x54ef4410004ae163_right": {
"order": 10000
},
"switch.relayswitch": {
"order": 10000
}
},
"layout_items": [
{
"id": "slot_69bc51604cd9a655530009",
"type": "ghost",
"order": 10006
},
{
"id": "slot_69bc51618576e557534578",
"type": "ghost",
"order": 10017
},
{
"id": "slot_69bc5173bcc31597082929",
"type": "ghost",
"order": 10027
},
{
"id": "slot_69bc51a07b397758519172",
"type": "ghost",
"order": 10037
},
{
"id": "slot_69bc51a18cd65501054136",
"type": "ghost",
"order": 10047
},
{
"id": "slot_69bc51a26a869826830118",
"type": "ghost",
"order": 10057
},
{
"id": "slot_69bc51a537f7a717770602",
"type": "ghost",
"order": 10067
}
],
"temperature_sensor_entity_id": "sensor.weather_temperature"
},
{
"id": "c7829e2bba524bc5aeb2662866058b57",
@ -672,8 +805,51 @@
},
"media_player.televizor_v_gostinoi": {
"visible": false
},
"light.kukhnia_svet_right": {
"order": 10020
},
"light.kukhnia_svet_center": {
"order": 10010
},
"light.kukhnia_svet_left": {
"order": 10000
},
"climate.air_conditioner_hsu_07hrm203_r3_in": {
"order": 10050
},
"remote.mitv_afmu0": {
"order": 10060
}
},
"layout_items": [
{
"id": "slot_69bc558692ea6091392089",
"type": "ghost",
"order": 10030
},
{
"id": "slot_69bc559079a55766958575",
"type": "ghost",
"order": 10040
},
{
"id": "slot_69bc55918cf6f412189279",
"type": "ghost",
"order": 10070
},
{
"id": "slot_69bc5592a0e1e292667710",
"type": "ghost",
"order": 10080
},
{
"id": "slot_69bc5593e7ac9622553048",
"type": "ghost",
"order": 10090
}
],
"temperature_sensor_entity_id": "sensor.kukhnia_temperatura_temperature"
},
{
"id": "prikhozhaia",
@ -719,9 +895,38 @@
"visible": false
},
"binary_sensor.reolink_silent_mode_active": {
"visible": true
"visible": true,
"order": 10060
},
"light.hall_switch": {
"order": 10000
},
"script.toggle_reolink_silent": {
"order": 10050
}
},
"layout_items": [
{
"id": "slot_69bc55a58f40a012297459",
"type": "ghost",
"order": 10010
},
{
"id": "slot_69bc55a68fa36878820522",
"type": "ghost",
"order": 10020
},
{
"id": "slot_69bc55a81f56c544279655",
"type": "ghost",
"order": 10030
},
{
"id": "slot_69bc55a9373e0445635380",
"type": "ghost",
"order": 10040
}
]
},
{
"id": "bf6b7f2827294fcea198262d5bf0a1b3",
@ -805,8 +1010,58 @@
},
"sensor.0xa4c138fe1cdd2a21_temperature": {
"visible": false
},
"light.roditeli_svet_left": {
"order": 10000
},
"light.roditeli_svet_right": {
"order": 10010
},
"remote.shield": {
"order": 10100
},
"switch.smallroom_switch": {
"order": 10050
}
},
"layout_items": [
{
"id": "slot_69bc555e1ad1c065023546",
"type": "ghost",
"order": 10020
},
{
"id": "slot_69bc55653ed9a111710448",
"type": "ghost",
"order": 10030
},
{
"id": "slot_69bc55703bf74010237960",
"type": "ghost",
"order": 10040
},
{
"id": "slot_69bc55713c237771552554",
"type": "ghost",
"order": 10060
},
{
"id": "slot_69bc55722858c536911665",
"type": "ghost",
"order": 10070
},
{
"id": "slot_69bc5573152eb561990860",
"type": "ghost",
"order": 10080
},
{
"id": "slot_69bc5574182c3344138189",
"type": "ghost",
"order": 10090
}
],
"temperature_sensor_entity_id": "sensor.0xa4c138fe1cdd2a21_temperature"
},
{
"id": "79c23414b5b840e1b83b09c0d970dcf3",
@ -1046,7 +1301,9 @@
"select.uvlazhnitel_led_brightness": {
"visible": false
}
}
},
"temperature_sensor_entity_id": "sensor.spalnya_temp_temperature",
"layout_items": []
},
{
"id": "chulan",

View File

@ -23,8 +23,8 @@ $appTitle = htmlspecialchars((string)($config['app']['title'] ?? 'Wall Panel'),
<script>
window.APP_BOOTSTRAP = <?= json_encode($bootstrap, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
</script>
<link rel="stylesheet" href="assets/app.css?v=0.19">
<script src="assets/app.js?v=0.19" defer></script>
<link rel="stylesheet" href="assets/app.css?v=0.25">
<script src="assets/app.js?v=0.25" defer></script>
</head>
<body>
<div class="app-shell">
@ -53,7 +53,7 @@ $appTitle = htmlspecialchars((string)($config['app']['title'] ?? 'Wall Panel'),
<div class="main-print-strip-slot" id="main-print-strip-slot"></div>
</div>
<header class="content-header">
<button class="icon-button icon-button--ghost content-header__back" id="selected-room-back" type="button" aria-label="Back">
<button class="icon-button icon-button--ghost content-header__back" id="selected-room-back" type="button" aria-label="Back" hidden>
<i class="mdi mdi-arrow-left"></i>
</button>
<div>
@ -61,6 +61,7 @@ $appTitle = htmlspecialchars((string)($config['app']['title'] ?? 'Wall Panel'),
<h1 class="content-header__title" id="selected-room-title">Загрузка</h1>
<div class="content-header__meta" id="selected-room-meta"></div>
</div>
<div class="content-header__actions" id="selected-room-actions"></div>
</header>
<section class="dashboard-grid" id="dashboard-grid">
@ -107,6 +108,21 @@ $appTitle = htmlspecialchars((string)($config['app']['title'] ?? 'Wall Panel'),
</div>
</div>
<div class="modal-backdrop" id="temperature-sensor-modal" aria-hidden="true">
<div class="temperature-sensor-modal" id="temperature-sensor-modal-panel" role="dialog" aria-modal="true" aria-labelledby="temperature-sensor-modal-title">
<div class="temperature-sensor-modal__header">
<div>
<div class="temperature-sensor-modal__eyebrow">Настройка комнаты</div>
<div class="temperature-sensor-modal__title" id="temperature-sensor-modal-title">Выбрать датчик температуры</div>
</div>
<button class="icon-button icon-button--ghost" id="temperature-sensor-modal-close" type="button" aria-label="Close">
<i class="mdi mdi-close"></i>
</button>
</div>
<div class="temperature-sensor-modal__body" id="temperature-sensor-modal-body"></div>
</div>
</div>
<div class="modal-backdrop" id="confirm-modal" aria-hidden="true">
<div class="confirm-modal" id="confirm-modal-panel" role="dialog" aria-modal="true" aria-labelledby="confirm-modal-title">
<div class="confirm-modal__header">

File diff suppressed because it is too large Load Diff

View File

@ -360,6 +360,7 @@ final class HomeAssistantClient
$normalized[] = [
'device_id' => $device['device_id'] ?? $device['id'] ?? $device['di'] ?? null,
'area_id' => $device['area_id'] ?? $device['ai'] ?? null,
'labels' => $this->normalizeWsLabels($device['labels'] ?? null),
'name' => $device['name'] ?? $device['en'] ?? null,
'manufacturer' => $device['manufacturer'] ?? $device['mf'] ?? null,
'model' => $device['model'] ?? $device['md'] ?? null,

Binary file not shown.

837
storage/battery_cache.json Executable file
View File

@ -0,0 +1,837 @@
{
"items": {
"sensor.garage_motion_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.garage_light_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.garage_door_motion_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.stair_up_motion_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.stair_down_motion_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.stair_light_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.door_sensor_2_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 90
},
{
"timestamp": 1773411344,
"value": 90
},
{
"timestamp": 1773414678,
"value": 90
},
{
"timestamp": 1773417954,
"value": 90
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 90
},
"sensor.wleak_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.0xa4c138433d675809_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 1
},
{
"timestamp": 1773411344,
"value": 1
},
{
"timestamp": 1773414678,
"value": 1
},
{
"timestamp": 1773417954,
"value": 1
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 1
},
"sensor.0xa4c138997cb4fdd1_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.printer_knopka_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 74
},
{
"timestamp": 1773411344,
"value": 74
},
{
"timestamp": 1773414678,
"value": 74
},
{
"timestamp": 1773417954,
"value": 74
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": -0,
"forecast_reason": "Нет заметного разряда",
"percent": 74
},
"sensor.lestnitsa_dvizhenie_2_etazh_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.spalnia_knopka_girliand_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 29
},
{
"timestamp": 1773411344,
"value": 29
},
{
"timestamp": 1773414678,
"value": 29
},
{
"timestamp": 1773417954,
"value": 29
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": -0,
"forecast_reason": "Нет заметного разряда",
"percent": 29
},
"sensor.ulitsa_temperatura_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 63
},
{
"timestamp": 1773411344,
"value": 63
},
{
"timestamp": 1773414678,
"value": 63
},
{
"timestamp": 1773417954,
"value": 63
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": -0,
"forecast_reason": "Нет заметного разряда",
"percent": 63
},
"sensor.0x44e2f8fffeb65d8e_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 70
},
{
"timestamp": 1773411344,
"value": 70
},
{
"timestamp": 1773414678,
"value": 70
},
{
"timestamp": 1773417954,
"value": 70
},
{
"timestamp": 1773418742,
"value": 65
},
{
"timestamp": 1773466239,
"value": 70
},
{
"timestamp": 1773466259,
"value": 60
}
],
"forecast_minutes_left": 10497,
"forecast_text": "≈ 7д 6ч до разряда",
"forecast_slope_per_hour": -0.2572,
"forecast_reason": null,
"percent": 45
},
"sensor.0x54ef4410009a6a11_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 95
},
{
"timestamp": 1773391484,
"value": 94
},
{
"timestamp": 1773394714,
"value": 95
},
{
"timestamp": 1773398043,
"value": 92
},
{
"timestamp": 1773401498,
"value": 95
},
{
"timestamp": 1773408087,
"value": 94
},
{
"timestamp": 1773411344,
"value": 94
},
{
"timestamp": 1773414678,
"value": 94
},
{
"timestamp": 1773417954,
"value": 94
},
{
"timestamp": 1773420793,
"value": 92
},
{
"timestamp": 1773424022,
"value": 94
},
{
"timestamp": 1773427250,
"value": 93
},
{
"timestamp": 1773430658,
"value": 95
},
{
"timestamp": 1773433662,
"value": 91
},
{
"timestamp": 1773437113,
"value": 94
},
{
"timestamp": 1773440355,
"value": 95
},
{
"timestamp": 1773443434,
"value": 94
},
{
"timestamp": 1773450072,
"value": 93
},
{
"timestamp": 1773453205,
"value": 92
},
{
"timestamp": 1773456219,
"value": 93
},
{
"timestamp": 1773459262,
"value": 92
},
{
"timestamp": 1773462330,
"value": 94
},
{
"timestamp": 1773465653,
"value": 92
},
{
"timestamp": 1773468992,
"value": 93
},
{
"timestamp": 1773472099,
"value": 94
},
{
"timestamp": 1773475444,
"value": 93
}
],
"forecast_minutes_left": 121234,
"forecast_text": "≈ 84д 4ч до разряда",
"forecast_slope_per_hour": -0.046,
"forecast_reason": null,
"percent": 93
},
"sensor.0x00124b0035558456_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 82
},
{
"timestamp": 1773411344,
"value": 82
},
{
"timestamp": 1773414678,
"value": 82
},
{
"timestamp": 1773417954,
"value": 82
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 82
},
"sensor.0xa4c13874f5fdfd2a_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 92.5
},
{
"timestamp": 1773411344,
"value": 92.5
},
{
"timestamp": 1773414678,
"value": 92.5
},
{
"timestamp": 1773417954,
"value": 92.5
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": -0,
"forecast_reason": "Нет заметного разряда",
"percent": 92
},
"sensor.0x54ef44100119db20_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 100
},
{
"timestamp": 1773411344,
"value": 100
},
{
"timestamp": 1773414678,
"value": 100
},
{
"timestamp": 1773417954,
"value": 100
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 100
},
"sensor.door_sensor_spalnya_battery": {
"loaded_at": 1773993995,
"points": [],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": null,
"forecast_reason": "Недостаточно истории",
"percent": 100
},
"sensor.0x0ceff6fffe6cffc4_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 45
},
{
"timestamp": 1773406716,
"value": 50
},
{
"timestamp": 1773406731,
"value": 45
},
{
"timestamp": 1773411344,
"value": 45
},
{
"timestamp": 1773414678,
"value": 45
},
{
"timestamp": 1773417954,
"value": 45
},
{
"timestamp": 1773418721,
"value": 50
},
{
"timestamp": 1773418743,
"value": 45
},
{
"timestamp": 1773466239,
"value": 50
},
{
"timestamp": 1773466260,
"value": 45
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0.0849,
"forecast_reason": "Заряд не падает",
"percent": 50
},
"sensor.0x0ceff6fffe6cdee0_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 0
},
{
"timestamp": 1773411344,
"value": 0
},
{
"timestamp": 1773414678,
"value": 0
},
{
"timestamp": 1773417954,
"value": 0
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 55
},
"sensor.0x705464fffe43dee0_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 50
},
{
"timestamp": 1773402386,
"value": 55
},
{
"timestamp": 1773402406,
"value": 50
},
{
"timestamp": 1773411344,
"value": 50
},
{
"timestamp": 1773414678,
"value": 50
},
{
"timestamp": 1773417954,
"value": 50
},
{
"timestamp": 1773418722,
"value": 55
},
{
"timestamp": 1773466239,
"value": 50
}
],
"forecast_minutes_left": 44180,
"forecast_text": "≈ 30д 16ч до разряда",
"forecast_slope_per_hour": -0.0475,
"forecast_reason": null,
"percent": 35
},
"sensor.0xa4c138259d164c22_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 88.5
},
{
"timestamp": 1773411344,
"value": 88.5
},
{
"timestamp": 1773414678,
"value": 88.5
},
{
"timestamp": 1773417954,
"value": 88.5
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 88.5
},
"sensor.0xa4c138fe1cdd2a21_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 87.5
},
{
"timestamp": 1773411344,
"value": 87.5
},
{
"timestamp": 1773414678,
"value": 87.5
},
{
"timestamp": 1773417954,
"value": 87.5
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": -0,
"forecast_reason": "Нет заметного разряда",
"percent": 85.5
},
"sensor.spalnya_temp_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 3
},
{
"timestamp": 1773411344,
"value": 3
},
{
"timestamp": 1773414678,
"value": 3
},
{
"timestamp": 1773417954,
"value": 3
},
{
"timestamp": 1773433548,
"value": 10
},
{
"timestamp": 1773440273,
"value": 3
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": null,
"forecast_reason": "Батарея уже разряжена",
"percent": 0
},
"sensor.kukhnia_temperatura_battery": {
"loaded_at": 1773993995,
"points": [
{
"timestamp": 1773389195,
"value": 90
},
{
"timestamp": 1773411344,
"value": 90
},
{
"timestamp": 1773414678,
"value": 90
},
{
"timestamp": 1773417954,
"value": 90
}
],
"forecast_minutes_left": null,
"forecast_text": null,
"forecast_slope_per_hour": 0,
"forecast_reason": "Нет заметного разряда",
"percent": 90
}
}
}

View File

@ -1,6 +1,6 @@
{
"active": false,
"sensor_entity_id": "binary_sensor.barn_all_occupancy",
"opened_at": 1773938797,
"opened_at": 1774006226,
"expires_at": null
}