hottis modbus relay 7
This commit is contained in:
@@ -448,13 +448,11 @@ def _transform_three_phase_powermeter_hottis_modbus_to_abstract(payload: str) ->
|
|||||||
"""Transform hottis_modbus three_phase_powermeter payload to abstract format.
|
"""Transform hottis_modbus three_phase_powermeter payload to abstract format.
|
||||||
|
|
||||||
Transformations:
|
Transformations:
|
||||||
- current_heating_setpoint -> target (as float)
|
- Direct mapping of all power meter fields
|
||||||
- local_temperature -> current (as float)
|
|
||||||
- system_mode -> mode
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
- zigbee2mqtt: {'current_heating_setpoint': 15, 'local_temperature': 23, 'system_mode': 'heat'}
|
- hottis_modbus: {'energy': 123.45, 'total_power': 1500.0, 'phase1_power': 500.0, ...}
|
||||||
- Abstract: {'target': 15.0, 'current': 23.0, 'mode': 'heat'}
|
- Abstract: {'energy': 123.45, 'total_power': 1500.0, 'phase1_power': 500.0, ...}
|
||||||
"""
|
"""
|
||||||
payload = json.loads(payload)
|
payload = json.loads(payload)
|
||||||
abstract_payload = {
|
abstract_payload = {
|
||||||
|
|||||||
@@ -150,11 +150,15 @@ class HomeAutomationClient {
|
|||||||
this.eventSource.close();
|
this.eventSource.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.eventSource = new EventSource(this.api('/realtime'));
|
const realtimeUrl = this.api('/realtime');
|
||||||
|
console.log('Connecting to SSE endpoint:', realtimeUrl);
|
||||||
|
this.eventSource = new EventSource(realtimeUrl);
|
||||||
|
|
||||||
this.eventSource.onmessage = (event) => {
|
this.eventSource.onmessage = (event) => {
|
||||||
|
console.log('Raw SSE event received:', event.data);
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
|
console.log('Parsed SSE data:', data);
|
||||||
|
|
||||||
// Normalize event format: convert API format to unified format
|
// Normalize event format: convert API format to unified format
|
||||||
const normalizedEvent = {
|
const normalizedEvent = {
|
||||||
@@ -163,6 +167,7 @@ class HomeAutomationClient {
|
|||||||
state: data.payload || data.state // Support both formats
|
state: data.payload || data.state // Support both formats
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('Normalized SSE event:', normalizedEvent);
|
||||||
onEvent(normalizedEvent);
|
onEvent(normalizedEvent);
|
||||||
|
|
||||||
// Notify all registered listeners
|
// Notify all registered listeners
|
||||||
@@ -172,12 +177,17 @@ class HomeAutomationClient {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to parse SSE event:', error);
|
console.error('Failed to parse SSE event:', error, 'Raw data:', event.data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.eventSource.onopen = (event) => {
|
||||||
|
console.log('SSE connection opened:', event);
|
||||||
|
};
|
||||||
|
|
||||||
this.eventSource.onerror = (error) => {
|
this.eventSource.onerror = (error) => {
|
||||||
console.error('SSE connection error:', error);
|
console.error('SSE connection error:', error);
|
||||||
|
console.log('EventSource readyState:', this.eventSource.readyState);
|
||||||
if (onError) {
|
if (onError) {
|
||||||
onError(error);
|
onError(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,6 +217,48 @@
|
|||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.phase-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-section h4 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-values {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: rgba(102, 126, 234, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value .value {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #667eea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phase-value .unit {
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.phase-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.state-badge {
|
.state-badge {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 8px 20px;
|
padding: 8px 20px;
|
||||||
@@ -298,7 +340,8 @@
|
|||||||
<script>
|
<script>
|
||||||
// Get device ID from URL
|
// Get device ID from URL
|
||||||
const pathParts = window.location.pathname.split('/');
|
const pathParts = window.location.pathname.split('/');
|
||||||
const deviceId = pathParts[pathParts.length - 1];
|
const deviceId = decodeURIComponent(pathParts[pathParts.length - 1]);
|
||||||
|
console.log('Device ID from URL:', deviceId);
|
||||||
|
|
||||||
// Device data
|
// Device data
|
||||||
let deviceData = null;
|
let deviceData = null;
|
||||||
@@ -366,6 +409,7 @@
|
|||||||
'thermostat': 'Thermostat',
|
'thermostat': 'Thermostat',
|
||||||
'contact': 'Kontaktsensor',
|
'contact': 'Kontaktsensor',
|
||||||
'temp_humidity_sensor': 'Temperatur & Luftfeuchte',
|
'temp_humidity_sensor': 'Temperatur & Luftfeuchte',
|
||||||
|
'three_phase_powermeter': 'Dreiphasen-Stromzähler',
|
||||||
'relay': 'Schalter',
|
'relay': 'Schalter',
|
||||||
'outlet': 'Steckdose',
|
'outlet': 'Steckdose',
|
||||||
'cover': 'Jalousie'
|
'cover': 'Jalousie'
|
||||||
@@ -797,9 +841,19 @@
|
|||||||
try {
|
try {
|
||||||
// Use API client's realtime connection
|
// Use API client's realtime connection
|
||||||
window.apiClient.connectRealtime((event) => {
|
window.apiClient.connectRealtime((event) => {
|
||||||
|
console.log('SSE event received:', event);
|
||||||
|
console.log('Current deviceId:', deviceId);
|
||||||
|
console.log('Event device_id:', event.device_id);
|
||||||
|
console.log('Device type:', deviceData.type);
|
||||||
if (event.device_id === deviceId && event.state) {
|
if (event.device_id === deviceId && event.state) {
|
||||||
|
console.log('Updating device state for:', deviceId);
|
||||||
|
console.log('Old state:', deviceState);
|
||||||
|
console.log('New state from event:', event.state);
|
||||||
deviceState = { ...deviceState, ...event.state };
|
deviceState = { ...deviceState, ...event.state };
|
||||||
|
console.log('Merged state:', deviceState);
|
||||||
updateUI();
|
updateUI();
|
||||||
|
} else {
|
||||||
|
console.log('SSE event ignored - not for this device or no state');
|
||||||
}
|
}
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.error('SSE connection error:', error);
|
console.error('SSE connection error:', error);
|
||||||
@@ -828,6 +882,9 @@
|
|||||||
case 'temp_humidity_sensor':
|
case 'temp_humidity_sensor':
|
||||||
updateTempHumidityUI();
|
updateTempHumidityUI();
|
||||||
break;
|
break;
|
||||||
|
case 'three_phase_powermeter':
|
||||||
|
updateThreePhasePowerUI();
|
||||||
|
break;
|
||||||
case 'cover':
|
case 'cover':
|
||||||
updateCoverUI();
|
updateCoverUI();
|
||||||
break;
|
break;
|
||||||
@@ -896,6 +953,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateThreePhasePowerUI() {
|
||||||
|
console.log('updateThreePhasePowerUI called with deviceState:', deviceState);
|
||||||
|
// Update overview
|
||||||
|
const totalPower = document.getElementById('total-power');
|
||||||
|
const energy = document.getElementById('energy');
|
||||||
|
|
||||||
|
console.log('Elements found - totalPower:', totalPower, 'energy:', energy);
|
||||||
|
|
||||||
|
if (totalPower && deviceState.total_power != null) {
|
||||||
|
console.log('Updating total power to:', deviceState.total_power);
|
||||||
|
totalPower.textContent = deviceState.total_power.toFixed(0) + ' W';
|
||||||
|
}
|
||||||
|
if (energy && deviceState.energy != null) {
|
||||||
|
console.log('Updating energy to:', deviceState.energy);
|
||||||
|
energy.textContent = deviceState.energy.toFixed(2) + ' kWh';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update phases
|
||||||
|
const phases = ['phase1', 'phase2', 'phase3'];
|
||||||
|
phases.forEach(phase => {
|
||||||
|
const power = document.getElementById(`${phase}-power`);
|
||||||
|
const voltage = document.getElementById(`${phase}-voltage`);
|
||||||
|
const current = document.getElementById(`${phase}-current`);
|
||||||
|
|
||||||
|
if (power && deviceState[`${phase}_power`] != null) {
|
||||||
|
power.textContent = deviceState[`${phase}_power`].toFixed(0);
|
||||||
|
}
|
||||||
|
if (voltage && deviceState[`${phase}_voltage`] != null) {
|
||||||
|
voltage.textContent = deviceState[`${phase}_voltage`].toFixed(1);
|
||||||
|
}
|
||||||
|
if (current && deviceState[`${phase}_current`] != null) {
|
||||||
|
current.textContent = deviceState[`${phase}_current`].toFixed(2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function updateCoverUI() {
|
function updateCoverUI() {
|
||||||
const slider = document.getElementById('position-slider');
|
const slider = document.getElementById('position-slider');
|
||||||
const value = document.getElementById('position-value');
|
const value = document.getElementById('position-value');
|
||||||
|
|||||||
Reference in New Issue
Block a user