Skip to content

models

eq3btsmart.models #

Models for the eQ-3 Bluetooth Smart Thermostat library.

DeviceData dataclass #

Bases: _BaseModel[_DeviceDataStruct]

Device data model.

Contains general information about the device that is affected by commands scheduled by the user.

Attributes:

Name Type Description
firmware_version(int)

The firmware version of the device.

device_serial(str)

The serial number of the device.

Source code in eq3btsmart/models.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@dataclass
class DeviceData(_BaseModel[_DeviceDataStruct]):
    """Device data model.

    Contains general information about the device that is affected by commands scheduled by the user.

    Attributes:
        firmware_version(int): The firmware version of the device.
        device_serial(str): The serial number of the device.
    """

    firmware_version: int
    device_serial: str

    @classmethod
    def _from_struct(cls, struct: _DeviceDataStruct) -> Self:
        return cls(
            firmware_version=struct.version,
            device_serial=struct.serial,
        )

    @classmethod
    def _struct_type(cls) -> type[_DeviceDataStruct]:
        return _DeviceDataStruct

firmware_version instance-attribute #

firmware_version

device_serial instance-attribute #

device_serial

Status dataclass #

Bases: _BaseModel[_StatusStruct]

Status model.

Contains the current status of the device.

Attributes:

Name Type Description
valve(int)

The current valve position.

target_temperature(float)

The target temperature.

operation_mode(Eq3OperationMode)

The operation mode.

is_away(bool)

Whether the device is in away mode.

is_boost(bool)

Whether the device is in boost mode.

is_dst(bool)

Whether the device is in daylight saving time.

is_window_open(bool)

Whether the window is open.

is_locked(bool)

Whether the device is locked.

is_low_battery(bool)

Whether the battery is low.

away_until(datetime | None

The time until the device is in away mode.

presets(Presets | None

The presets of the device.

Source code in eq3btsmart/models.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@dataclass
class Status(_BaseModel[_StatusStruct]):
    """Status model.

    Contains the current status of the device.

    Attributes:
        valve(int): The current valve position.
        target_temperature(float): The target temperature.
        operation_mode(Eq3OperationMode): The operation mode.
        is_away(bool): Whether the device is in away mode.
        is_boost(bool): Whether the device is in boost mode.
        is_dst(bool): Whether the device is in daylight saving time.
        is_window_open(bool): Whether the window is open.
        is_locked(bool): Whether the device is locked.
        is_low_battery(bool): Whether the battery is low.
        away_until(datetime | None): The time until the device is in away mode.
        presets(Presets | None): The presets of the device.
    """

    valve: int
    target_temperature: float
    _operation_mode: Eq3OperationMode
    is_away: bool
    is_boost: bool
    is_dst: bool
    is_window_open: bool
    is_locked: bool
    is_low_battery: bool
    away_until: datetime | None = None
    presets: Presets | None = None

    @property
    def operation_mode(self) -> Eq3OperationMode:
        """The operation mode."""
        if self.target_temperature == EQ3_OFF_TEMP:
            return Eq3OperationMode.OFF

        if self.target_temperature == EQ3_ON_TEMP:
            return Eq3OperationMode.ON

        return self._operation_mode

    @property
    def valve_temperature(self) -> float:
        """The valve temperature.

        The valve temperature is calculated based on the valve position and the target temperature.
        """
        return (1 - self.valve / 100) * 2 + self.target_temperature - 2

    @classmethod
    def _from_struct(cls, struct: _StatusStruct) -> Self:
        return cls(
            valve=struct.valve,
            target_temperature=struct.target_temp,
            _operation_mode=Eq3OperationMode.MANUAL
            if struct.mode & struct.mode.MANUAL
            else Eq3OperationMode.AUTO,
            is_away=bool(struct.mode & struct.mode.AWAY),
            is_boost=bool(struct.mode & struct.mode.BOOST),
            is_dst=bool(struct.mode & struct.mode.DST),
            is_window_open=bool(struct.mode & struct.mode.WINDOW),
            is_locked=bool(struct.mode & struct.mode.LOCKED),
            is_low_battery=bool(struct.mode & struct.mode.LOW_BATTERY),
            away_until=struct.away,
            presets=Presets._from_struct(struct.presets) if struct.presets else None,
        )

    @classmethod
    def _struct_type(cls) -> type[_StatusStruct]:
        return _StatusStruct

valve instance-attribute #

valve

target_temperature instance-attribute #

target_temperature

is_away instance-attribute #

is_away

is_boost instance-attribute #

is_boost

is_dst instance-attribute #

is_dst

is_window_open instance-attribute #

is_window_open

is_locked instance-attribute #

is_locked

is_low_battery instance-attribute #

is_low_battery

away_until class-attribute instance-attribute #

away_until = None

presets class-attribute instance-attribute #

presets = None

operation_mode property #

operation_mode

The operation mode.

valve_temperature property #

valve_temperature

The valve temperature.

The valve temperature is calculated based on the valve position and the target temperature.

Presets dataclass #

Bases: _BaseModel[_PresetsStruct]

Presets model.

Contains the presets of the device.

Attributes:

Name Type Description
window_open_temperature(float)

The temperature when the window is open.

window_open_time(timedelta)

The time the window is open.

comfort_temperature(float)

The comfort temperature.

eco_temperature(float)

The eco temperature.

offset_temperature(float)

The offset temperature.

Source code in eq3btsmart/models.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
@dataclass
class Presets(_BaseModel[_PresetsStruct]):
    """Presets model.

    Contains the presets of the device.

    Attributes:
        window_open_temperature(float): The temperature when the window is open.
        window_open_time(timedelta): The time the window is open.
        comfort_temperature(float): The comfort temperature.
        eco_temperature(float): The eco temperature.
        offset_temperature(float): The offset temperature.
    """

    window_open_temperature: float
    window_open_time: timedelta
    comfort_temperature: float
    eco_temperature: float
    offset_temperature: float

    @classmethod
    def _from_struct(cls, struct: _PresetsStruct) -> Self:
        return cls(
            window_open_temperature=struct.window_open_temp,
            window_open_time=struct.window_open_time,
            comfort_temperature=struct.comfort_temp,
            eco_temperature=struct.eco_temp,
            offset_temperature=struct.offset,
        )

    @classmethod
    def _struct_type(cls) -> type[_PresetsStruct]:
        return _PresetsStruct

window_open_temperature instance-attribute #

window_open_temperature

window_open_time instance-attribute #

window_open_time

comfort_temperature instance-attribute #

comfort_temperature

eco_temperature instance-attribute #

eco_temperature

offset_temperature instance-attribute #

offset_temperature

Schedule dataclass #

Schedule model.

The schedule is a list of days with a list of hours for each day. Each hour contains the target temperature and the time when the next change occurs.

Attributes:

Name Type Description
schedule_days(list[ScheduleDay])

The schedule days.

Source code in eq3btsmart/models.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
@dataclass
class Schedule:
    """Schedule model.

    The schedule is a list of days with a list of hours for each day.
    Each hour contains the target temperature and the time when the next change occurs.

    Attributes:
        schedule_days(list[ScheduleDay]): The schedule days.
    """

    schedule_days: list[ScheduleDay] = field(default_factory=list)

    def merge(self, other_schedule: Self) -> None:
        """Merge another schedule into this schedule.

        Args:
            other_schedule(Schedule): The other schedule to merge.
        """
        for other_schedule_day in other_schedule.schedule_days:
            schedule_day = next(
                (
                    schedule_day
                    for schedule_day in self.schedule_days
                    if schedule_day.week_day == other_schedule_day.week_day
                ),
                None,
            )

            if not schedule_day:
                self.schedule_days.append(other_schedule_day)
                continue

            schedule_day.schedule_hours = other_schedule_day.schedule_hours

    @classmethod
    def _from_bytes(cls, data: bytes) -> Self:
        return cls(schedule_days=[ScheduleDay._from_bytes(data)])

    def __eq__(self, __value: object) -> bool:
        """Check if the schedule is equal to another schedule.

        Args:
            __value(object): The value to compare.
        """
        if not isinstance(__value, Schedule):
            return False

        week_days_to_compare = [
            schedule_day.week_day
            for schedule_day in self.schedule_days
            if len(schedule_day.schedule_hours) > 0
        ]
        week_days_to_compare.extend(
            [
                schedule_day.week_day
                for schedule_day in __value.schedule_days
                if len(schedule_day.schedule_hours) > 0
            ]
        )

        for week_day in week_days_to_compare:
            schedule_day = next(
                (
                    schedule_day
                    for schedule_day in self.schedule_days
                    if schedule_day.week_day == week_day
                ),
                None,
            )
            other_schedule_day = next(
                (
                    schedule_day
                    for schedule_day in __value.schedule_days
                    if schedule_day.week_day == week_day
                ),
                None,
            )

            if schedule_day is None or other_schedule_day is None:
                return False

            other_schedule_day = next(
                (
                    other_schedule_day
                    for other_schedule_day in __value.schedule_days
                    if other_schedule_day.week_day == schedule_day.week_day
                ),
                None,
            )

            if schedule_day != other_schedule_day:
                return False

        return True

schedule_days class-attribute instance-attribute #

schedule_days = field(default_factory=list)

merge #

merge(other_schedule)

Merge another schedule into this schedule.

Parameters:

Name Type Description Default
other_schedule(Schedule)

The other schedule to merge.

required
Source code in eq3btsmart/models.py
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def merge(self, other_schedule: Self) -> None:
    """Merge another schedule into this schedule.

    Args:
        other_schedule(Schedule): The other schedule to merge.
    """
    for other_schedule_day in other_schedule.schedule_days:
        schedule_day = next(
            (
                schedule_day
                for schedule_day in self.schedule_days
                if schedule_day.week_day == other_schedule_day.week_day
            ),
            None,
        )

        if not schedule_day:
            self.schedule_days.append(other_schedule_day)
            continue

        schedule_day.schedule_hours = other_schedule_day.schedule_hours

ScheduleDay dataclass #

Bases: _BaseModel[_ScheduleDayStruct]

Schedule day model.

Attributes:

Name Type Description
week_day(Eq3WeekDay)

The week day.

schedule_hours(list[ScheduleHour])

The schedule hours.

Source code in eq3btsmart/models.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
@dataclass
class ScheduleDay(_BaseModel[_ScheduleDayStruct]):
    """Schedule day model.

    Attributes:
        week_day(Eq3WeekDay): The week day.
        schedule_hours(list[ScheduleHour]): The schedule hours.
    """

    week_day: Eq3WeekDay
    schedule_hours: list[ScheduleHour] = field(default_factory=list)

    @classmethod
    def _from_struct(cls, struct: _ScheduleDayStruct) -> Self:
        return cls(
            week_day=struct.day,
            schedule_hours=[
                ScheduleHour(
                    target_temperature=hour.target_temp,
                    next_change_at=hour.next_change_at,
                )
                for hour in struct.hours
            ],
        )

    @classmethod
    def _struct_type(cls) -> type[_ScheduleDayStruct]:
        return _ScheduleDayStruct

    def __eq__(self, __value: object) -> bool:
        """Check if the schedule day is equal to another schedule day.

        Args:
            __value(object): The value to compare.
        """
        if not isinstance(__value, ScheduleDay):
            return False

        if self.week_day != __value.week_day:
            return False

        if len(self.schedule_hours) != len(__value.schedule_hours):
            return False

        return all(
            hour == other_hour
            for hour, other_hour in zip(self.schedule_hours, __value.schedule_hours)
        )

week_day instance-attribute #

week_day

schedule_hours class-attribute instance-attribute #

schedule_hours = field(default_factory=list)

ScheduleHour dataclass #

Schedule hour model.

Attributes:

Name Type Description
target_temperature(float)

The target temperature.

next_change_at(time)

The time when the next change occurs.

Source code in eq3btsmart/models.py
329
330
331
332
333
334
335
336
337
338
339
@dataclass
class ScheduleHour:
    """Schedule hour model.

    Attributes:
        target_temperature(float): The target temperature.
        next_change_at(time): The time when the next change occurs.
    """

    target_temperature: float
    next_change_at: time

target_temperature instance-attribute #

target_temperature

next_change_at instance-attribute #

next_change_at